I'm using storyboard in my ipad application and successfully able to do transitions, use segues etc.
Currently I am showing pop over view controller on click of a button. I want to detect when the pop over dismisses.
How can I do it?
Here is what I did:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"popover"])
{
UIStoryboardPopoverSegue *pop = (UIStoryboardPopoverSegue*)segue;
pop.popoverController.delegate = self;
}
}
UIPopoverController
Now with my revelation that you're talking about a UIPopoverController, here are the steps:
Setup the UIPopoverController with an appropriate delegate (I'm assuming the "sender" view controller)
Have your "sender" conform to the UIPopoverControllerDelegate
Implement the – popoverControllerDidDismissPopover: message and have any detection logic here
Implement - prepareForSegue:sender: and use the segue's destinationController to both get a reference and set the delegate, something like below:
((MyViewController*)segue.destinationController).delegate = self;
Modal View Controller
Add a delegate to the view controller that is being presented
Name your segue if you haven't already
Have your base view controller implement - prepareForSegue:sender: (refer to the UIViewController documentation)
Assign the sending view controller as the modal view controller's delegate in prepareForSegue:sender:
Call a desired method on the delegate immediately before or after you call dismissModalViewControllerAnimated:
That is how I would approach this. I would also recommend having a formal protocol to conform your sending view controller with.
Create a segue in view controller:
#property (strong, nonatomic) UIStoryboardPopoverSegue* popSegue;
In XIB, create an identifier called "popover" for the view.
In Interface, write the following code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [[segue identifier] isEqualToString:#"popover"] )
{
//[[segue destinationViewController] setDelegate:self];
NSLog(#"%#",[[segue destinationViewController] viewControllers]);
self.popSegue = (UIStoryboardPopoverSegue*)segue;
.
.
.
}
Write the following code to dismiss the pop over by coding:
[self.popSegue.popoverController dismissPopoverAnimated:YES];
Since UIStoryboardPopoverSegueis deprecated in iOS 9, you can use a UIStoryboardPopoverPresentationSegue.
Then in prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)you can set the delegate like this:
Swift:
if let identifier = segue.identifier where identifier == "showPopover" {
let destVC = segue.destinationViewController as! UIViewController
destVC.popoverPresentationController?.delegate = self
}
An Objective-C code for the question is below.
if ([segue.identifier isEqualToString:#"home_login"])
{
UIViewController *dest = segue.destinationViewController;
dest.popoverPresentationController.delegate = self;
}
- (BOOL) popoverPresentationControllerShouldDismissPopover:(UIPopoverPresentationController *)popoverPresentationController
{
return NO;
}
Related
I have a project where I have set up a protocol to pass information back from one TableViewController to a ViewController. Everything worked fine and as expected, but I decided to embed in a Navigation Controller to the TableViewController so I could add a "DONE" barButtonItem to dismiss the Controller when the user is done. Since embedding in the navigation controller, the button works well, the TablieViewController looks identical, but none of its features and methods that use the Protocol and Delegate work, and if I remove the NavigationController everything works. Could someone explain how I can fix this issue? I am fairly new to iOS and objective c.
Here is the prepareForSegue method in the NoteViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController isKindOfClass:[ToolTableViewController class]]) {
ToolTableViewController *targetVC = segue.destinationViewController;
targetVC.toolDelegate = self;
targetVC.autoCorrectIsOn = self.autoCorrectIsOn;
targetVC.undoAvailable = self.undoAvailable;
targetVC.redoAvailable = self.redoAvailable;
}
}
ToolTableViewController.h
#protocol ToolTableViewControllerDelegate <NSObject>
#property (weak, nonatomic) id <ToolTableViewControllerDelegate> toolDelegate;
ToolTableViewController.m - example of a method called
-(void)clearInputText{
// NSLog(#"Clear Method Selected");
[self.toolDelegate didClearInputText];
}
NoteViewController.m
-(void)didClearInputText{
self.noteTextView.text = #"";
[self dismissViewControllerAnimated:YES completion:nil];
}
Since your table view controller is embedded in a navigation controller, it's the navigation controller that will be the destination view controller of the segue. Also, it would be better to use the identifier of the segue for the if statement, rather than the class of the destination view controller (I'm using "SegueToTable" as the identifier, change that to whatever you put for the identifier). Therefore, prepareForSegue should look like this,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"SegueToTable"]) {
UINavigationController *nav = segue.destinationViewController;
ToolTableViewController *targetVC = nav.topViewController;
targetVC.toolDelegate = self;
targetVC.autoCorrectIsOn = self.autoCorrectIsOn;
targetVC.undoAvailable = self.undoAvailable;
targetVC.redoAvailable = self.redoAvailable;
}
}
Your delegate methods are called just fine (based on the sample you pasted).
Since your controllers are embedded in a navigation controller now, you should use:
[self.navigationController popViewControllerAnimated:YES]
Before, you were presenting your controllers modally, that's why dismissViewController worked fine then but not now ( in the context of a nav controller).
I'm new using storyboards and I'm facing this situation: A uinavigationcontroller contains a view controller (root controller) which contains ten buttons linked each one of then through storyboard to the same view controller.
The behavior of the second view controller depends on the button tapped in the first view controller, but how can identify which button is tapped (like tag value) and pass this info to second view controller?
Thank you.
To add on Daniel's answer:
First, add a public property onto your secondVC that is accessible from your first VC:
#interface SecondViewController : UIViewController
#property (nonatomic) int buttonTagClicked;
#end
You need to set tags up on your UIButtons. This is either done in storyboard or programmatically in your code. I would create a generic IBAction that each button is linked to. You can extract the tag off the button through the sender parameter later.
- (IBAction)buttonClicked:(id)sender
{
[self performSegueWithIdentifier:#"pushSegue" sender:sender];
}
That is linked up to
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"pushSegue"]) {
SecondViewController *destinationVC = (SecondViewController *)[segue destinationViewController];
UIButton *selectedButton = (UIButton *)sender;
destinationVC.buttonTagClicked = selectedButton.tag;
}
}
You can set a segueIdentifier for each connection. Then in your ViewController you could trigger an action based on the identifier you set.
e.g.:
If you select your connection in storyboard you can name it:
And in your ViewController you can trigger an action based on the identifier like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segue1"]) {
UIViewController *destinationVC = [segue destinationViewController];
destinationVC.property = ...;
}
}
Then I call this method I want to have segue. Is it right?
- (void)showMapViewController {
[self performSegueWithIdentifier:#"MapViewController" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"MapViewController"])
{
UINavigationController *navigationController = segue.sourceViewController;
LoginViewController *loginViewController = [[navigationController viewControllers] objectAtIndex:0];
[loginViewController performSegueWithIdentifier:#"MapViewController" sender:self];
}
}
You have to create segue in storyboard and give a unique identifier. It can be perform automaticaly if you set this on some button action, e.g. if you create a segue on button action an want to push a navigation controller. When ever that button will be push segue will be perform automaticlay. In this case if you want to do some custom coding to set some properties etc. then you should implement this delegate method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"segueIdentifier"])
{
// Here you can get source and destination view controllers and can perform some custom tasks.
}
}
You can also perform segue by code if you want to, for that you have to call this method.
[self performSegueWithIdentifier:#"segueidentifer" sender:self];
For this you have to create a segue in storyboard by clt+drag from one view controller to other(source -> destination) and give an identifier.
When doing a modal segue, does the originating ViewController get discarded after the segue is performed? I am setting the destination controller's delegate to the source ViewController, but when the destination ViewController.viewDidLoad, the self.delegate is nil...
The following code will produce the log message "ListViewController.viewDidLoad: My delegate is nil :("
[Source] MapViewController:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"mapToList"]){
NSLog(#"MapViewController.prepareForSegue: Segue mapToList being called, setting LisViewController's delegate to myself");
[segue.destinationViewController setDelegate:self];
if(!self){
NSLog(#"MapViewController.prepareForSegue: I am nil.");
} else {
NSLog(#"MapViewController.prepareForSegue: I am NOT nil.");
}
}
}
[Destination] ListViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
if(!self.delegate){
NSLog(#"ListViewController.viewDidLoad: My delegate is nil :(");
} else {
NSLog(#"ListViewController.viewDidLoad: My delegate populated");
}
}
Your code seems correct, the only thing I have done differently is test this in a skeleton framework I have that is a tableviewcontroller nested in a navigationcontroller. I just tested with the following code and it works fine for me:
RootViewController .h:
#interface RootTableViewController : UITableViewController <newTest>
Prepare for Segue (in rootViewController):
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"segueToModalView"]){
[segue.destinationViewController setDelegate:self];
}
}
Top of Modal View Controller .h:
#protocol newTest <NSObject>
-(void) hello;
#end
Property Declaration in Modal View:
#property (nonatomic, strong) id <newTest> delegate;
ViewDidLoad in Modal View:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.delegate);
}
My NSLog of self.delegate properly prints out and my code appears to be more or less the same as yours. Is your property declared correctly?
This is an old question but I came upon it when running into the same issue myself. Couple of things here:
To the guy who didn't understand why someone would want to use a Nav controller with a modal display - its to get the benefit of the nav bar without having to embed a UINavigationBar into your own view controller like tw airball did.
To solve the problem without resorting to what tw airball did remember that the destination view controller for the segue in this case is the navigation controller...not the view controller embedded in the nav.
So the fix is in your prepareForSeque:
UINavigationController *navController = segue.destinationViewController;
MyRealDestViewController *myRealDestViewController = (MyRealDestViewController)navController.topViewController;
myRealDestViewController.delegate = self;
If the segue is to a NavigationController then the destinationViewController loses the delegate.
I got around this problem by having the modal segue into the destinationViewController, and then adding NavigationBar and Bar Buttons to simulate the navigation controller (I assume you wrapped the destinationViewController in a NavigationController for the "done" and "cancel" buttons).
Set the delegate as normal in the rootViewController:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"segueToModalView"]){
[segue.destinationViewController setDelegate:self];
}
}
Hope that helps
Before storyboards I was able to set delegates and datasources just by dragging an outlet to a class. With storyboards, I cannot drag the outlet to another view controller; there is no destination that will respond to it.
If I click on a view controller object, I am able to see the class owner at the bottom, but as soon as I select the other view controller containing the outlet, the old selection is gone, so I cannot connect the two.
Is this Apple's way of saying we should only connect them programmatically?
Correct. Set the delegate or other data in your prepareForSegue:sender: method. Here is an example:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Check the segue identifier
if ([segue.identifier isEqualToString:#"showDetail"])
{
// Get a reference to your custom view controller
CustomViewController *customViewController = segue.destinationViewController;
// Set your custom view controller's delegate
customViewController.delegate = self;
}
}
If your storyboard segue destination View Controller is an UIViewController then #Marco answer is right. But if your destination View Controller is a UINavigationViewController then you have to get the UIViewController from UINavigationViewController :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Check the segue identifier
if ([segue.identifier isEqualToString:#"chooseCategoryType"])
{
// Get a reference of your custom view controller if your segue connection is an UIViewController
// CustomViewController *customViewController = segue.destinationViewController;
// Get a reference of your custom view controller from navigation view controller if your segue connection is an UINavigationViewController
CustomViewController *customViewController = [[[segue destinationViewController] viewControllers] objectAtIndex:0];
// Set your custom view controller's delegate
customViewController.delegate = self;
}
}