In this tutorial, we’ll show how to pass data from a view controller to a popover, and have data passed passed back from a button click, as well as dismissing the popover. The benefit is that it allows you to programmatically save or alter data, and pass it back. This is in contrast to a popover being dismissed by merely tapping outside the popover area of the screen. These are typical elements that would be used together for an iPad or Universal project. Since popovers are not available to iPhone (as of iOS 6 anyway), this would be used with an iPad storyboard.
I’ve posted the project files for starting, and the completed final.
Starting Point
The project starts with an iPad storyboard. The initial view controller is a UIViewController called RootViewController. It already has connected IBOutlets for a UITextField and a UIButton. There is a another UIViewController called PopViewController, which is the popover. A segue has been created from the button in the RootViewController to PopViewController. There is a UITextField and a UIButton on the popover to send back to the root, which are connected outlets. The view controller class files have been created and connected in the storyboard.
Segue Identifiers
It’s important to always give a name to your segues, as it is common for one view controller to have multiple segues. When doing segue delegation, you want to manage which segue, and differentiate which code goes with what segue. In the storyboard, select your segue, and go to the Attributes Inspector. Name the segue “segPop.”
Setting IBActions
The first thing we want to do is create IBActions for the buttons. In PopViewController.h, add a new function at the bottom.
@property (strong, nonatomic) IBOutlet UITextField *tfNewData;
– (IBAction)goBack: (id)sender;
@end
In PopViewController.m, we want to add the implementation of the function.
– (IBAction)goBack: (id)sender {
}
@end
Now connect the button click to the IBAction. One way to do this is to go to the storyboard, select the button in the Document outline, and ctrl-drag to Pop View Controller. Select the inaction goBack.
You can always go to the Connections Inspector to see (or set) your connections.
Delegation
Delegation is a programming pattern that allows an object to work in coordination with another object. A delegate will receive event messages, or callbacks, from the delegating object. Using delegation is so common in iOS development, that understanding it is a requirement.
Creating a Protocol
In order to do a delegate callback function, you need to establish a protocol, which will outline what callbacks are available. In PopViewController.h, add some protocol lines.
#import <UIKit/UIKit.h>@protocol PopViewControllerDelegate; // Declare a protocol name
@interface PopViewController : UIViewController
…
@end
@protocol PopViewControllerDelegate <NSObject>
@required
– (void)dismissPop: (NSString *)value;
@end
It is good practice to name your delegate as “<classname>Delegate”, making it easy to understand to code. In protocols, methods can be required or optional. If your class implements a delegate protocol, and does not include a method to handle the callback, Xcode will give a warning that the implementation is incomplete.
Creating a Local Delegate
The calling object needs to set itself as the delegate in the delegation relationship. We’ll establish a property to hold this. Back to PopViewController.h:
The property is set to a weak reference, for ARC compliance, and good memory management. Because this property stores a reference from a different class instance, if t gets destroyed somewhere else, this instance can free the memory. Make sure to synthesize any properties you add, so in PopViewController.m:
Sending a Delegate Method
Now that we have a property to store the delegate, we can add the code to send a delegate event. We’ll do that in our IBAction of the button in the popover. Add a line to our goBack function in PopViewController.m:
– (IBAction)goBack: (id)sender {
[delegate dismissPop:[tfNewData text]]; // make delegate callback here
}
This code is going to take the text of our UITextField and send it back to the delegate, which is our RootViewController. Remember that delegate is an id, or pointer, back to the RootViewController instance. This tells us where to send the delegate message.
Preparing for Incoming Data
In order to prepare to receive data from RootViewController, we want to add a string property to receive the data, as the segue will not have direct access to the UITextField. In PopViewController.h, add the following:
Make sure to synthesize in PopViewController.m:
Since PopViewController is a UIViewController, it is automatically able to do its own delegate methods. We want to add one to PopViewController.m:
[tfNewData setText:strPassedValue];
}
Becoming a Delegate
Go to RootViewController.h, and add the following:
#import “PopViewController.h” // add header to gain access to its protocol
@interface RootViewController : UIViewController <PopViewControllerDelegate, UIPopoverControllerDelegate>
First, you must import the header file, which is how to reach the delegate protocol. This will help code completion make this easy. We want to be sign up for two delegate protocols. The second one is so we can receive callback messages from the popover. Second, we have an implementation in RootViewController.m to receive the callback:
– (void)dismissPop: (NSString *)value {
[tfUserText setText:value]; // populates data from popover
}
This function allows us to capture and display the data string sent back from the popover via delegation.
Managing the Popover
We want to be able to send data to our popover, as well as be able to programmatically close our popover, so in RootViewController.h, add some properties:
@property (strong, nonatomic) PopViewController *pvc;
Synthesize them in our implementation RootViewController.m:
@synthesize pvc;
Passing Data in a Segue
Since we signed up for the popover delegate protocol, we want to implement the prepareForSegue function. Add the lines:
-(void)prepareForSegue: (UIStoryboardSegue *)segue sender: (id)sender {
if ([[segue identifier] isEqualToString:@”segPop”]) {
currentPopoverSegue = (UIStoryboardPopoverSegue *)segue;
pvc = [segue destinationViewController];
[pvc setDelegate:self];
[pvc setStrPassedValue:[tfUserText text]];}
}
In this code, we are looking to make sure that we have the proper segue, which is why we set its identifier property in the storyboard. Next, we want to keep a pointer to the popover segue, so we can dismiss it after we have processed the delegate callback data. We also want a pointer to the view controller that is the popover, because we set ourselves as the delegate. Without setting the delegate, the delegate callbacks would go nowhere. Lastly, we want to pass data into the PopViewController,which allows us a round trip of data back and forth from both objects.
Dismissing the Popover
As the final step, we want to add a line of code to our delegate callback function in RootViewController.m:
[tfUserText setText:value]; // populates data from popover
[[currentPopoverSegue popoverController] dismissPopoverAnimated: YES]; // dismiss the popover
}
The final line uses our pointer to the segue, so we can have the popoverController dismiss or popover instance. Notice we do not send anything to the PopViewController instance. When we created a segue that was of type popover, a popoverController was automatically created for us, and that is what want to access to do the actual dismissing.
Using Data Passed via Segue
Now that we have data coming across the segue, we want to use it in our popover. In PopViewController.h, we want to add In PopViewController.m, add the following UIViewController delegate function:
[tfNewData setText:strPassedValue];
}
Conclusion
Run the project in the iPad Simulator, and you should be able to pass data back and forth between the text fields. The button on the popover should also dismiss the popover. Okay that was a lot of steps, but you can pass data from segues, delegate callbacks, and control your popovers, all at the same time.