Delegates an important concept in ObjC/SWIFT or any other coding language. I know that delegates are used to pass messages from one class to another class especially when we want to pass message back to a view controller from where we have just moved to some other view controller.
I was searching for more technical answer and searched a lot about this, and here is what I got what I feel might be the exact answer -
By the rules of MVC, we need a method to return a value. Where in a
called instance can we go back to the class calling it? With an
encapsulated class we can’t. There is no way to send that revised
model back to the original controller without breaking encapsulation
or MVC. The new view controller does not know anything about the class
that called it. We look stuck. If we try to make a reference directly
to the calling controller, we may cause a reference loop that will
kill our memory. Simply put, we can’t send things backwards.
But the explanation says something about
Where in a
called instance can we go back to the class calling it? With an
encapsulated class we can’t. There is no way to send that revised
model back to the original controller without breaking encapsulation
or MVC.
So exactly what does this para mean. Can any one please explain this in a more simple way taking the following code as reference -
VC2 -
import UIKit
protocol myDelegate : class
{
func sendItems(name:NSString)
}
class EnterViewController: UIViewController
{
weak var delegate: myDelegate?
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendData(sender: AnyObject)
{
delegate?.sendItems(nameTextfield.text!)
self.navigationController?.popViewControllerAnimated(true)
}
VC2
import UIKit
class DisplayViewController: UIViewController,myDelegate
{
#IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func sendItems(name: NSString) {
self.nameLabel.text = name as String
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let destinationVC = segue.destinationViewController as! EnterViewController
if segue.identifier == "enterDetail"
{
destinationVC.delegate = self
}
}
}
Thanks.
In simple terms, Delegate is a representative of Class which works on behalf of class. If we compare in real world, delegates to foreign countries go to represent their government and have all controls and powers. Similarly, here delegates has all the control that an object of class will have and working on behalf of Class.
Now delegate in specific is an object assigned by class to notify the event. This can be acheived by NSNotification too. But the difference is Delegates can intercept the event but NSNotification cant.
Here in your code:
You have assigned DestinationVC's delegate to DisplayViewController.
Now DisplayviewController will notify all the event whatever you want to notify to Class DestinationVC and also intercept in between event.
In your case you are calling sendItems of DestinationVC.
I am sorry for my bad explanation but I guess you would have get the basic idea.
Related
So consider this case, i have a UIViewController that contains a simple UICollectionView, but the Delegate & DataSource protocols are separated NSObject's from the UIViewController.
It looks something like this
class MainCollctionViewDelegate: NSObject, UICollectionViewDelegate
class MainCollectionViewDataSrouce: NSObject, UICollectionViewDataSource
And i use them inside my UIViewController like this,
lazy var CVDelegate = MainCollctionViewDelegate()
lazy var CVDataSource = MainCollectionViewDataSrouce()
//MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
collectionView.registerCell(PlainCell.self) // register custom cell Nib into collection View.
collectionView.delegate = CVDelegate //Set Deleagte
collectionView.dataSource = CVDataSource // Set data Source
}
Is this approach going to cause any memory leaks in the future ? considering i will implement an injection to fill the data source of the CollectionView to be something like this in the future.
MainCollectionViewDataSrouce(with: Foo) // Foo is some data to populate the collection with.
Is there a better practice to this ? considering I'am trying to achieve
the minimum code writing (redundancy).
Note: this also applies for UITableViewDelegate & UITableViewDataSource
Is this approach going to cause any memory leaks in the future ?
Not right now.
Your memory graph will look like:
So here no memory cycles and no reasons to leak memory.
Important. If you add reference from DataSource / Delegate on your viewController, make sure it is weak reference, otherwise you will create memory cycle.
Note. You can add strong references from DataSource / Delegate on collectionView, since collectionView have weak references on dataSource and delegate. So no cycle as well
Side note
Better to register cells in data source, since "only" data source know what types of cell will be used.
Your question is rather vague, but in general, that is a very common practice. We use this pattern a lot in our company:
class MainCollectionViewController: UIViewController {
lazy var dataSource: UICollectionViewDataSource = self
lazy var delegate: UICollectionViewDelegate = self
static func with(dataSource: UICollectionViewDataSource, delegate: UICollectionViewDelegate) -> MainCollectionViewController {
let vc = MainCollectionViewController()
vc.dataSource = dataSource
vc.delegate = delegate
return vc
}
}
extension MainCollectionViewController: UICollectionViewDataSource {
// code
}
extension MainCollectionViewController: UICollectionViewDelegate {
// code
}
The two primary uses are for unit testing and for passing data to the view controller. The tester can inject custom data source and delegate at test time:
let testVC = MainCollectionViewController.with(dataSource: ..., delegate: ...)
// do test
Or passing data to it:
// In another view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? MainCollectionViewController {
destinationVC.dataSource = ...
destinationVC.delegate = ...
}
}
As to memory leak, this pattern is generally safe but obviously someone will run into a memory problem once in a while. Your mileage may vary.
For some reason the delegate method is not being called in the main View Controller. I was looking for another answers here, but non of them were helpful for me. Am I missing something here? (I shortened my original code for simplicity sake)
Main View Controller:
class VC: ParserDelegate {
var dataSource = Parser()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
dataSourse.loadAndParse()
}
func didReceiveDataUpdates(store: [WeatherModel]) {
print("Delegate method triggered.")
}
}
Protocol:
protocol ParserDelegate: class {
func didReceiveDataUpdates(store: [WeatherModel])
}
My delegate class:
class Parser {
weak var delegate: ParserDelegate?
func loadAndParse() {
var store = [WeatherModel]()
// Doing something
delegate?.didReceiveDataUpdates(store: store)
}
}
The delegate pattern is being applied correctly here, but one thing that might go wrong here: In your main View Controller you are instantiating a new Parser object and store it in „dataSource“:
var dataSource = Parser()
And when setting your main View Controller as its delegate
dataSource.delegate = self
your main View Controller gets notified as the delegate of this new instance you just created. That means: If an instance of your Parser() class jumps into (assure with debugger, if it actually does)
loadAndParse()
it might be another object and so this parser object has no actual delegate. If this is the issue here, you might consider and outlet in order to be able to talk to this specific Parser() class directly. Hope this helps.
You can also edit this line:
from:
dataSource.delegate = self
dataSourse.loadAndParse()
to:
dataSource.delegate = self
dataSource.loadAndParse()
I read a lot about the delegates but in practice I cannot use it properly.
Description: I have A: UIViewController, B: UIView, C: UIViewController. I want to run segue from A: UIViewController to the C: UIViewController from the inside of B: UIView.
I've tried:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class B: UIView { ... }
where in my A: UIViewController:
override func viewDidLoad() {
B().delegate = self
}
func runSegue(identifier: String) {
self.performSegueWithIdentifier(identifier, sender: self)
}
and trying to call it via:
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = nil
a!.runSegue("goToMainPage")
}
but I'm sure that I do not use it properly. Can anyone help me with it? I do not want just an answer. Please describe me it concept shortly
Delegates are just a Design Pattern that you can use in a number of ways. You can look at the Apple Frameworks to see how and where to use delegates as examples. A table view delegate is probably the best known delegate in UIKit.
Delegates serve as a callback mechanism for code to communicate with an instance of an unknown class without knowing more than that that instance will respond to the methods of the delegate protocol.
An alternative to a delegate is to use a closure (what we used to call a block in Objective-C). When to use one vs. the other is a matter of taste. There are a couple of rules of thumb, like for instance outlined here.
What you are doing is, IMO, the proper way to use delegates. You separate the view functionality from the View Controller's functionalities via a delegate, and so the contract for your view is clear: the user needs to respond to the delegate method.
Your code works and is correct. I made a quick implementation here: https://github.com/kristofvanlandschoot/DelegateUsage/tree/master
The main difference from your example, and maybe that's the place where you made a mistake is the third part of your code where you should write something like:
#IBAction func send(sender: AnyObject) {
delegate?.runSegue("segueAB")
}
There are multiple errors in your code, for example:
Here you are creating a new B, and setting A as a delegate of that new instance, no the one you actually want
override func viewDidLoad() {
«B()».delegate = self
}
And here you are creating force unwrapping a nil value
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = «nil»
«a!».runSegue("goToMainPage")
}
If what you want to do is tell A to perform a segue to C, from inside B, all you need to do is to call performSegueWithIdentifier on A
For example:
class B: UIView {
weak var referenceToA: UIViewController? = nil // set this somewhere
#IBAction func send(sender: AnyObject) {
guard let a = referenceToA else {
fatalError("you didn't set the reference to a view controller of class A")
}
a.performSegueWithIdentifier("goToMainPage", sender: self)
}
}
I want to update the label in the DetailViewController everytime I selected a tableRow in the MasterViewController. To achieve this, I designed a delegate, which I have in the MasterVC
protocol TestTableViewControllerDelegate {
func selectedRow(selectedCar : Car)
}
class TestTableViewController: UITableViewController {
...
var delegate : TestTableViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = DetailViewController()
The delegate works just fine, (it is implemented correctly in the DetailVC), it can pass values from TestTableVC to DetailVC and also correctly do println(), which prints a new Car.model String to the console every time I select a row in the TTVC.
The DetailVC looks like this (shortened):
class DetailViewController: UIViewController, TestTableViewControllerDelegate {
#IBOutlet var textLabel: UILabel!
var theCar : Car? {
didSet(newCar) {
refreshUI()
}
}
override func viewDidLoad() {
super.viewDidLoad()
refreshUI()
}
func selectedRow(selectedCar : Car) {
theCar = selectedCar
refreshUI()
}
func refreshUI() {
textLabel?.text = theCar!.model
}
}
I can achieve any kind of action with my delegate, expect for refreshing the UI. I have tried numerous ways, this is my latest attempt. Before that, I tried setting the textLabel's text property directly within the delegate method, didn't work. This problem only occurs when working with the UI-elements. I know it has something to do with the view not being loaded yet, but why does my refreshUI() function not work at all?
I am still a beginner, so any tip or help would be much appreciated!
A workaround I've used is to cerate a properly in the delegate and pass the value to it instead of the UI element. When the view loads I update the label's text properly with the value of the delegates property. I would think there's a better way to do this (I'm new to programming) but this is the best soultion I've come up with so far. Will update with sample code soon.
I have been pulling my hair out trying to get this 'Delegate' thing to work in Swift for an App I am working on.
I have two files: CreateEvent.swift and ContactSelection.swift, where the former calls the latter.
CreateEvent's contents are:
class CreateEventViewController: UIViewController, ContactSelectionDelegate {
/...
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
override func viewDidLoad() {
super.viewDidLoad()
/...
contactSelection.delegate = self
}
func updateInvitedUsers() {
println("this finally worked")
}
func inviteButton(sender: AnyObject){
invitedLabel.text = "Invite"
invitedLabel.hidden = false
toContactSelection()
}
/...
func toContactSelection() {
let contactSelection = self.storyboard?.instantiateViewControllerWithIdentifier("ContactSelectionViewController") as ContactSelectionViewController
contactSelection.delegate = self
self.navigationController?.pushViewController(contactSelection, animated: true)
}
ContactSelection's contents are:
protocol ContactSelectionDelegate {
func updateInvitedUsers()
}
class ContactSelectionViewController: UITableViewController {
var delegate: ContactSelectionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.updateInvitedUsers()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Stuff
self.delegate?.updateInvitedUsers()
}
}
What am I doing wrong? I am still new and don't fully understand this subject but after scouring the Internet I can't seem to find an answer. I use the Back button available in the Navigation Bar to return to my CreateEvent view.
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
This is instantiating a view controller directly, and the value never gets used. Since it looks like you're using storyboards, this isn't a good idea since none of the outlets will be connected and you'll get optional unwrapping crashes. You set the delegate of this view controller but that's irrelevant as it doesn't get used.
It also isn't a good idea because if you do multiple pushes you'll be reusing the same view controller and this will eventually lead to bugs as you'll have leftover state from previous uses which might give you unexpected outcomes. It's better to create a new view controller to push each time.
In your code you're making a brand new contactSelection from the storyboard and pushing it without setting the delegate.
You need to set the delegate on the instance that you're pushing onto the navigation stack.
It's also helpful to pass back a reference in the delegate method which can be used to extract values, rather than relying on a separate reference in the var like you're doing.
So, I'd do the following:
Remove the var contactSelection
Add the delegate before pushing the new contactSelection object
Change the delegate method signature to this:
protocol ContactSelectionDelegate {
func updateInvitedUsers(contactSelection:ContactSelectionViewController)
}
Change your delegate calls to this:
self.delegate?.updateInvitedUsers(self)