I have a ViewController, this view container has a class which creates 2 container views, and adds a table to the first container and a HashtagPicker for the second.
The hashTagPicker has a function which is called whenever a change to the selected hashTags happens.
question: i want to call a update table function whenever a tag is changed. How can i call a function from the hashtagclass which is defined in the class that contains the containers?
I personally like the delegate approach over notifications - the latter solution almost always leads to confusing architecture. Sadly, the example for the delegate approach, which is also the accepted answer, is even worse - it basically opens an opportunity for memory leaks. I'll explain. In the accepted solution, ParentView
is holding a strong reference to HashtagPicker
and, in turn, HastagPicker
is holding a strong reference to ParentView
, this creates a retain cycle and means neither of the controllers will be picked up by ARC and be deinitialized. So, if you are, for example, presenting ParentView
from some other view and you keep going to ParentView
and back, you will keep spawning new instances of ParentView
(and HashtagPicker
) with old ones still occupying memory.
Now, how this should have been done. I'll use exactly the same names as in the accepted answer.
The protocol should be defined like so:
// note the ": class" part
protocol HashTagPickerDelegate: class {
func picked(hashtag: String)
}
If we specify class
, it means the protocol can only be used on classes. This will allow use to create weak reference, which otherwise would have been impossible.
class HashtagPicker: UIViewController {
// if HashTagPickerDelegate wouldn't be limited to class,
// we couldn't have made a weak reference here!
weak var delegate: HashTagPickerDelegate?
// at some point, you call the delegate, it can be anywhere, this is just an example
@IBAction func tappedHashtag(_ sender: Any) {
delegate?.picked(hashtag: "bla")
}
}
Now we have a weak reference to delegate, so there is not retain cycle and ARC can clean up everything nicely!
I'll throw in the rest of the code to have this as a complete answer:
class ParentView: UIViewController {
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// we are presenting the nested controller
if segue.identifier == "SegueHastagPickerContainer",
let destinationController = segue.destination as? HashtagPicker {
destinationController.delegate = self
}
}
}
extension ParentView: HashTagPickerDelegate {
func picked(hashtag: String) {
// we just got info from the child controller, do something with it!
}
}