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
Related
If I have multiple view controllers being presented and dismissed in any order, can I be sure that iOS calls viewWillAppear methods in the right order (i.e. order of appearance)?
I cannot find any specific information about this in the documentation.
I think this is all you need to know about viewWillAppear from the docs:
This method is called before the view controller's view is about to be added to a view hierarchy and before any animations are configured for showing the view. You can override this method to perform custom tasks associated with displaying the view. For example, you might use this method to change the orientation or style of the status bar to coordinate with the orientation or style of the view being presented. If you override this method, you must call super at some point in your implementation.
Only thing that comes to mind that might not be absolutely clear is that this callback is called on the presenting view controller when presented view controller is going to be dismissed (so presenting view controller is going to appear again).
Therefore if A is a root, A.viewWillAppear will be called before it will appear on the screen. Then, if A presents B, just before B becomes visible, B.viewWillAppear will be called. And when B is being dismissed, A.viewWillAppear will get called again, since its view will appear again.
viewWillAppear() is called the first time the view is displayed and it is also called when the view is displayed again, so it can be called multiple times during the life of the view controller object.
It’s called when the view is about to appear as a result of the user tapping the back button, closing dialog, or when the view controller’s tab is selected in a tab bar controller, or a variety of other reasons. Make sure to call super.viewWillAppear() at some point in the implementation
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
}
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.
I am navigating between screens in my iOS application.
BaseView.NavigationController.ViewControllers
As I switch screens, I keep a reference to the previous screen in a static variable.
At some point, one of my items gets removed from BaseView.NavigationController.ViewControllers even though it's still a valid viewcontroller and IsLoaded is still set to True/YES.
When I use (pardon my C#/MonoTouch)
BaseView.NavigationController.PopToViewController(CurrentViewController,false);
to show it again, I get NSInternalInconsistencyException Reason: Tried to pop to a view controller that doesn't exist. This is understandable because it's no longer in the ViewController collection.
The way I am switching screens is I am keeping a reference to he various screens and calling a common method to show the screen. In that method I use this logic to determine if I should push or pop.
if (CurrentViewController.IsViewLoaded)
{
BaseView.NavigationController.PopToViewController(CurrentViewController,false);
}
else
{
BaseView.NavigationController.PushViewController(CurrentViewController,true);
}
My question is where did it go and why would it have been removed from ViewControllers collection and when it's StillLoaded=true/YES?
If I understand correctly, you're using NavigationController.PopToViewController(controller); to navigate back to a certain view controller but keep a reference of the View Controllers that are popped from the navigation stack.
What I think is happening is because you're keeping a reference to these View Controllers, they're still in memory and thus the IsViewLoaded property is still true despite the View Controller not actually existing on the navigation stack.
Rather than using the IsViewLoaded property, you should check whether the View Controller exists in the NavigationController.ViewControllers array, if it does then Pop to it, if it doesn't then push it.
E.g.
if (BaseView.NavigationController.ViewControllers.Contains(CurrentViewController))
{
BaseView.NavigationController.PopToViewController(CurrentViewController,false);
}
else
{
BaseView.NavigationController.PushViewController(CurrentViewController,true);
}
Edit
So you mention you'd like a view to persist on the navigation stack. Well, using PopToViewController will remove ALL View Controllers between the TopViewController and the specified Controller.
In order to achieve what you're wanting, you could directly manipulate the NavigationControllers.ViewControllers array. Only problem with this is you'll lose the nice animations that the Push/Pop methods provide.
// Changes order of View Controllers currently in the stack. You can also add/remove
// controllers using this method.
NavigationController.ViewControllers = new UIViewController[]{
NavigationController.ViewControllers[1],
NavigationController.ViewControllers[0],
NavigationController.ViewControllers[3],
NavigationController.ViewControllers[2]};
I need to save my data by calling a method I already have when a viewController is popped using the back button created by the UINavigationController.
Is there a way to get a delegate callback or a notification I didn't see anything in the documentation?
In your viewWillDisappear method, you can check the property:
[self isMovingFromParentViewController]
to find out if the view is disappearing as a result of being popped off the stack or not.
You will be notified that the view will be disappearing, with the view controller method viewWillDisappear:, however, this will be called each time the view is moved offscreen, whether that means the controller is popped or a new controller is pushed, or whatever else may cause your view to disappear.
Perhaps a better design would be to save your data in your controllers dealloc method. Normally, a navigation controller is the owner of a view pushed into it's stack, so popping it usually causes it to deallocate. This isn't always the case though and depends on how you've written your app.