Friday, March 20, 2015

Delegation on iOS

This tutorial is a simple basic idea to show the concept of delegation on iOS, using Objective-C.
Delegation is usually not something a beginner would use, so this tutorial would be targeting intermediate users, which also means it assumes you know your way around Xcode and Objective-c at a reasonable level.

Final result

So here's what the final app looks like:

The app has 2 views, first view and second view. The first view has a label and a button, and the second view has a textField and a button. When you push the button on the second view, the text inside the textField gets placed in the label of the first view.
Pretty simple, but its a great demo of how delegation works.
Let' start by setting up the UI.

Setting up the UI

Create a new single view project in Xcode using storyboards, objective-c and for iPhone.
Delete the default ViewController class (so "ViewController.h" and "ViewController.m").
In the storyboard, make the single ViewController you have be embedded in a navigation controller. Add a second ViewController on your storyboard. Your storyboard should now have 3 elements: 1 NavigationController and 2 ViewControllers. Also, the first ViewController should be linked to the navigationController.
In the first ViewController add 2 labels and 1 button.
In the second ViewController add 1 label, 1 textField and 1 button.
I changed the color in the background of each ViewController to make it more obvious in this demo.
Create a new push segue between the first view controller and the second view controller (not the buttons but the controllers themselves) and name it "segueSecondView".
At this point your UI should look something like this:

Create the classes for the ViewControllers

Create 2 new classes: FirstViewController and SecondViewController, both being a subclass of UIViewController. In your storyboard, make your first view controller be of custom class FirstViewController and your second view controller be of custom class SecondViewController.
Link the IBActions and IBOutlets from the UI to the classes as normal.

Create the protocol

After all the initial setup has been done, its time to start with the actual delegation.
In the SecondViewController.h, we need to do 2 things:
  • Declare a protocol
  • Create a variable of the newly created protocol
Here's what the code looks like. I will explain it below:
//  SecondViewController.h
//  Test_Delegate
//  Created by Eduardo Flores on 3/20/15.
//  Copyright (c) 2015 Eduardo Flores. All rights reserved.

#import <UIKit/UIKit.h>

// declare the delegate
@protocol SecondViewDelegate 

- (void) textFromSecondView:(NSString *)text;

@end // end protocol

// Declare the created delegate into our class
@interface SecondViewController : UIViewController

// Original declaration in our header
@property (nonatomic, weak) id<SecondViewDelegate>  delegate;

@property (weak, nonatomic) IBOutlet UITextField *textField_enterSomeText;
- (IBAction)button_sendTextBack:(id)sender;

@end //end interface

The code above has 2 main sections: the protocol and the interface.

The protocol declares methods that whoever will use this protocol will have to implement. This is pretty much like interfaces in Java. Just declare what the "delegate" has to do, but don't implement it.
The name of the protocol usually, by convention, uses the word "Delegate" and it usually has the name of the class where it is declared.

The interface should look very common to you, with the exception of this line
@property (nonatomic, weak) id<SecondViewDelegate> delegate;
In here we're just declaring a varilable to the protocol created above so we can refer to it from another class.

Passing the data back using the new delegate
What we want to happen is to take the text that's inside the textfield and pass it back to the FirstViewController, and this will happen when we push the button we created in the SecondViewController. After we pass the data back to the FirstViewController, we're going to pop the view to return the UI to the FirstViewController.

So with that idea, code for the button in the SecondViewController.m looks like this:
- (IBAction)button_sendTextBack:(id)sender
    // Send the data to whatever class implements the delegate
    [self.delegate textFromSecondView:textField_enterSomeText.text];
    // pop the view controller
    [[self navigationController]popViewControllerAnimated:YES];

So that's all we have to do to declare a delegate in out SecondViewController, and to send data back to whoever or whatever class decides to declare itself as a delegate for the SecondViewDelegate.
At this point we're done with SecondViewController.h and SecondViewController.m

Implementing the delegate

Now that we have a class declaring it has a delegate, it is time to have another class declaring itself as delegate of the SecondViewDelegate.

In the FirstViewController.h we need to import the SecondViewController.h and say our class implements the SecondViewDelegate.
Here's what the FirstViewController.h looks like:
//  FIrstViewController.h
//  Test_Delegate
//  Created by Eduardo Flores on 3/20/15.
//  Copyright (c) 2015 Eduardo Flores. All rights reserved.

#import "SecondViewController.h"

@interface FIrstViewController : UIViewController <SecondViewDelegate>

@property (weak, nonatomic) IBOutlet UILabel *label_receivedText;

- (IBAction)button_startSecondView:(id)sender;


Now we have declared the FirstViewController class is a delegate of SecondViewDelegate, so what's the logic in FirstViewController?
  1. We need to push our view from FirstViewController to SecondViewController 
  2. We need to get a reference to the SecondViewController
  3. We have to get a reference then to the "delegate" variable in the SecondViewController
  4. Since we're a delegate of SecondViewDelegate, we need to implement all of the methods in SecondViewDelegate. This means we have to implement the "- (void)textFromSecondView:(NSString *)text" method
  5. And finally, we need to set the text in the label of the FirstViewController with whatever text is "coming" from the SecondViewDelegate. This won't be visible or applied until we see the FirstViewController the second time, after we return from SecondViewController
Step 1.
We're going to push to the SecondViewController when we push the 1 button in our FirstViewController, so the implementation of the button would look like this:
- (IBAction)button_startSecondView:(id)sender
    [self performSegueWithIdentifier:@"segueSecondView" sender:nil];

Steps 2 and 3.
Since we're using storyboard we can take advantage of the "prepareForSegue" method. In here we get a reference to the SecondViewController and we declare the FirstViewController as the delegate of the SecondViewDelegate. This is what it looks like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    SecondViewController *svc = (SecondViewController *)[segue destinationViewController];
    svc.delegate = self;

Steps 4 and 5.
Since we're now the delegate of SecondViewDelegate we need to implement the "- (void)textFromSecondView:(NSString *)text"method, and in here we're going to set our label from the FirstViewController with the String coming from the delegate.
So it looks like this:
- (void)textFromSecondView:(NSString *)text
    label_receivedText.text = text;

So there you have it. The SecondViewController now takes some text and sends it to the FirstViewController through the SecondViewDelegate.

The complete source code of this demo can be found on github, right here.


No comments:

Post a Comment