All,
I'm trying to perform segues based on the identity of the currently displayed view controller. Essentially, I've given my VC's storyboard ID's and now want to access these in code. So, essentially I want some logic whereby if existing view controller is first, I want to perform firstSegue and if it's second, I want to perform secondSegue and so on. Also, my VC's are part of a navigation controller and I know that the navigation controller has a property where i can view the present view controller or something like that. But I wasnt sure what it was. Can somebody help me out? Again, I foresee my code being something like:
(IBAction)firstButtonPressed:(id)sender
{ if (presentviewcontroller ==a) // If the current view controller Is A
{
[self performSegueWithIdentifier:#"segueA" sender:self];}
if(storyboard.viewcontroller==b)//If the current view controller is B
{
[self.performSegueWithIdentifier:#"segueB" sender:self];}
}
Can someone help me out with some code?
I'm not sure I understand your setup, but if it's what I think it is, then you don't need to know from which controller the button was pressed. Even though the 2 controllers inherit from a common ancestor, they are still separate instances, and only the segue that is connected to that instance will be called. So, each of your controllers could have a segue with the same identifier, lets say button1Segue. Then the code in AncestorController could just be:
-(IBAction)firstButtonPressed:(UIButton *)sender{
[self performSegueWithIdentifier:#"button1Segue" sender:self];
}
The correct segue will be performed because whichever instance's button was pressed, only that instance's segue will go.
If you were to set different tags for each view in interface builder, you could use the following
if (self.view.tag == 1) {
NSLog(#"something");
}
I successfully use this method in my app for different views, and it works well.
Related
I'm trying to save some data upon user leaving this specific view controller (either by hitting nav back button or different tab bar button or exiting the app). However, there is one segue going forward, in this case I don't want to save the data yet, it will get saved on the next view controller with additional data.
How can I determine in viewWillDisappear: (or anywhere else) that user is leaving the view controller, but differentiate that it is not through the segue? I've looked at self.isBeingDismissed() and self.isMovingFromParentViewController() and I can't seem figure out a good solution. Any insight is greatly appreciated, thanks!
You might want to try this method instead of viewWillDisappear-
fun willMoveToParentViewController(parent : UIViewController?)
{
super.willMoveToParentViewController(parent);
if parent == nil {
//This means the current controller is getting popped out of the nav stack
}
}
Documentation:
Called just before the view controller is added or removed from a
container view controller.
Since navigation controller is a container controller, when it removes the top most controller, it should call this method with parent value as nil.
HTH
In storyboard we have great feature that allow us to make Show (e.g. push). So seems the logic is next:
If we don't have navigation controller then view controller will use present modal logic. My question is there any inverse action that I can use with Show?
I have a UIButton that close current view controller screen:
- (IBAction)onTappedCloseButton:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
But in case if I don't have navigation controller, how can I simple use inverse action to go back? So my solution is to check if self.navigationController is nil then use dismissing option:
[self dismissViewControllerAnimated:YES completion:nil];
But maybe there is another cool solution like Show (e.g push). But Close (e.g. pop)?
Yes, you can use an unwind segue to go back, and it will be the reverse of whatever the forward segue was.
You have two options on how to do this:
1) The Unwind segue
To make an unwind segue you have to add a method in the view controller you want to "unwind" to with the following format:
-(IBAction)someSelectorName:(UIStoryboardSegue *)sender;
You will then be able to drag from your UIButton up to the "exit" icon in your storyboard.
Wire it up to the selector you just defined and UIKit will figure out how to get back to that view controller without you having to write any code. This can be especially useful as it can figure out when it needs to call -dismissViewControllerAnimated: more than once and can call those methods successfully. It can even unwind from within a view controller embedded in a navigation controller when the view controller you're unwinding to has the navigation controller presented on top of it. (i.e. it will do a dismissViewController instead of a pop to unwind)
2) The Custom unwind method
Say you don't want to or cant trigger this action from a storyboard. There is still an option and its detailed over at this question here:
Whats the programmatic opposite of showViewController:sender:
The gist is you can write your own generic dismiss method by implementing categories on the UIKit container View controllers (or on your own container)
I have a home-screen, which has a push segue to various other view controllers. However, when I return from one (and only one) of these view controllers back to the home screen, I want the home-screen to reload one of its functions. The viewWillAppear method is useful (if I implement it on the home screen) because it is called when I return to the home screen, but is there something I can add, that will essentially check "was I sent back to the home-screen from -this specific- view Controller"? Not very eloquently worded I apologise.
The way I am doing this at the moment is to define a universal boolean variable, which becomes true when I am on the special view controller, then in the viewWillAppear method, if the boolean == true I reload the function. I don't like using these universal variables though, it feels error prone.
n.b. When I say universal variable, I mean one that is defined above the class, this may not be the proper term
When you are sent from another view controller back to the home screen, are you using an unwind segue? If so, the home view controller should have a method of this form:
- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue{
}
Inside this method, you can check if the sourceViewController is of a certain class:
if([unwindSegue.sourceViewController isKindOfClass:[otherTypeOfviewController class]]){
//perform the tasks you need carried out
}
I'm having trouble piecing this all together. I have a view controller that opens up another (pushes it on to the navigation stack). On that presented view controller, the user enters a value in a text view. When the user pushes the back button in the navigation, I want to be able to pass the value that they entered in the text view back to the presenting controller.
I've looked for a way to use unwind segue with the back button but haven't found anything. When I create my back button (programmatically) I use initWithTitle:style:target:action but I'm not sure how in implementing the action method that I'll be able to access the value set in the presented controller. Might have to use a delegate to link the two, but not sure of the exact integration point for this scenario.
I feel like I'm so close here and a little help would get me there. Thanks!
The two most common models to use for this interaction are for the child view controller to have either a delegate or a completion block. Either would be set in the prepareForSegue method. My personal preference is the completion block method just because it keeps code contained, but ymmv.
There are also multiple models for detecting when your child view controller is dismissed and you need to invoke the delegate and/or completion:
Use a custom back button. Not a fan of this as it can be an issue to create a back button that really looks and acts like the Apple original, especially if supporting iOS 6 and iOS 7.
Hook viewDidDisappear and see if you're still in the navigation controller's viewControllers array. This is better as the back button works right, but it still feels kind of hokey.
Use the UINavigationBarDelegate method navigationBar:shouldPopItem: This is attractive, especially if you have other validation that needs to happen like checking for saved/unsaved values. To implement this you'll have to subclass UINavigationController and forward the method to your child view controller.
EDIT: Details on Option 2:
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if(![self.navigationController.viewControllers containsObject:self])
{
// We're not still in the navigation stack so we must've been
// popped. If we were pushed, viewDidDisappear would be called
// but viewControllers containsObject:self would be true
}
}
EDIT: Clarified Option 3: in your navigation controller subclass
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
UIViewController* top = self.topViewController;
if([top respondsToSelector:#selector(navigationBar:shouldPopItem:)])
return [(id)top navigationBar:navigationBar shouldPopItem:item];
return [super navigationBar:navigationBar shouldPopItem:item];
}
Then you can implement navigationBar:shouldPopItem: in the classes that need the functionality.
the back button does not actually comes up with any event associated with itself so that you can pass the values between the previous and to be Popped ViewController.
You would have to implement Delegate pattern to pass values. In this case as you cant catch when backButton is pressed, you need to use custom leftBarButtonItem or use a image with < in itself.
I just have a quick question about recommended ways to implement a master-detail view hierarchy in iOS--the kind where selecting a row in a table on one screen pushes a details view for that item onto the navigation stack.
Specifically, should I reuse the same instance of the details view controller and just change its target and reload it each time, or should I instantiate a new instance of the view controller each time?
I'd prefer the first method, as it just seems generally more efficient, but I'm having trouble figuring out how to set the target and do the reload (especially the first time, when the view controller has not yet even been initialized--I'm using storyboards and that pretty much handles all of the initialization itself).
Or perhaps instead of setting the target on the child view controller, I could set it on the parent, such that each time the child view controller is shown, it reloads itself based on the parent selection? That actually sounds like the best bet so far, just looking for tips/warnings from anyone who's run into this before.
First, there's nothing wrong with creating a new view controller each time. If you use segues, that's what you'll get, since segues always instantiate new controllers. The detail controller will be deallocated when you pop or dismiss it anyway, so it won't persist.
If you want to use the same controller, you have to do your push or presentViewController in code. You can still setup the controller in the storyboard. Give it an identifier, but don't connect it up with a segue. In code, you check for the existence of your controller (you'll need a property for it), and if it doesn't exist, create it.
if (! self.detailController) {
DetailController *dvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyIdentifier"];
}
self.dvc.whateverProperty = self.somePropertyIWantToPass; // pass some date to it
[self.navigationController pushViewController:dvc animated:YES completion:nil];