We have a container view controller and want to be able to call "PerformSelector" on one of the "sub" view controllers in that container, right after starting a transition, i.e.
[self navigateSubViewControllerTo:newSubViewController];
... some time later, elsewhere in the stack, a selector will be performed on the top visible VC
[subViewController performSelector:#selector(foo)];
The call to transitionFromViewController happens in navigateSubViewController.Unfortunately, since transitionFromViewController happens asynchronously, we are finding that the performSelector call gets applied to the "before" sub view controller, not newSubViewController. I.e. it is happening before the transition happens.
Any thoughts on how to have performSelect not happen until the sub view controller transition happens?
You can just call performSelector in the completion block of
transitionFromViewController:toViewController:duration:options:animations:completion:
UIViewController provides a callback beginAppearanceTransition:animated: which is exactly for this purpose. Simply implement it in your subviewcontrollers and your good to go ;)
Related
I have seen many similar questions on SO and as far as I can see all of them suggest that we should not push/pop any views while either the current view 'viewWillAppear' has not being called or the 'isViewLoaded' call returns false.
In my instance, I try to push a new view in the current view 'viewWillAppear' method and before I push it I also call [self isViewLoaded] to see if the current view is loaded (which is) yet I still keep getting the above error.
The only way I was able to solve this (which is a hack) is to sleep for few seconds before I call the push on a different non UI thread.
Since sleeping for few seconds before the call actually makes the pushing works, I assume that although both 'viewWillAppear' is already called and [self isViewLoaded] is true, it is still be sometimes too early to push/pop another view (maybe do to animation still not complete?).
I'm I missing something? Is there any other method other than 'viewWillAppear' or [self isViewLoaded] that will indicate %100 that the view has completely loaded and all animations are complete so I can safely push/pop other views?
you could try it in viewDidAppear.
however, what scenario/design you had in mind when implementing this?
IMO, it is a bad design to immediately push a new view controller from the view that isn't loaded yet.
I use the following to open a new view controller
ViewController *mainMenu = [self.storyboard instantiateViewControllerWithIdentifier:#"mainview"];
[self.navigationController pushViewController:mainMenu animated:NO];
When the mainview is active on the screen the application still runs old view controller (methods) in the background.
How i can stop the old view controller from running or make it inactive?
First, let's ask ourselves this: what kind of methods run on a view controller?
I'm going to categorize the methods that are part of a UIViewController subclass into 4 categories (though there's clearly overlap among these categories).
Life-cycle methods. These are methods such as viewDidLoad, viewWillAppear:, etc. that run as part of the view controller's life-cycle. None of these methods should be running if the view is "in the background" other than, arguably, viewDidLoad, viewWillAppear: which are technically called before the view has come to the foreground, and viewDidDisappear: which is technically called just after the view has left the foreground. So we don't really need to worry about these.
Delegate methods. Your view controller could delegate objects. The delegate methods are called on your view controller whenever the object they're delegating calls them. In the majority of the cases, we're talking about delegating some sort of UI element which is part of the view controller. This UI element would be "in the background" any time the view controller is, so it shouldn't be calling delegate methods.
NSTimer called methods. NSTimer can keep a strong reference to an object, and keep that object alive until the fire time ticks over and it calls the specified method on that object. These methods are certainly likely to be called even if the view controller is in the background, but if you want to stop the timer from running if the view controller's view isn't visible, then just invalidate it in viewDidDisappear:.
Miscellaneous methods. These methods don't necessarily into any of the above categories (though they could). Most of the time these methods are called are from within the view controller themselves, so if nothing is being called from one of the first three categories, it will be rare that this category is called (unless the method is public).
So you've got methods running "in the background" on your view controller. What category do they fall in?
If they're in the first category, then either you're calling a life-cycle method manually from somewhere (which I can't recommend against enough--there's no good reason to do this), or perhaps you don't understand the UIViewController life cycle.
If they're in the second category, then it's most likely good that they're being called. If the Object A is being delegated by View Controller A and Object A is still working on some process and still requires delegation, then it's extraordinarily important that View Controller A is still responding to the delegation methods. If you don't want this to happen, you need to stop Object A from requiring delegation.
If they're in the third category, invalidate your NSTimer objects when it's appropriate. NSTimer doesn't just magically not do what you scheduled it to do. You have to invalidate it in viewDidDisappear if you don't want it to call methods on the view controller when its view is not visible.
If they're in the fourth category, start by making sure you've not got public methods that you're calling manually from outside the view controller. If you do, well stop calling them. If you don't, go double check the first three categories.
If you mean that the view controller that was active before is still executing code, I'd suggest doing the following to remove it from view and execution before pushing:
ViewController *mainMenu = [self.storyboard instantiateViewControllerWithIdentifier:#"mainview"];
[self.navigationController popViewControllerAnimated: NO]; // Takes the old one away
[self.navigationController pushViewController:mainMenu animated:NO];
I have a navigation based application and in the child view I have a button, tapping on which results in calling the popToRootViewController method.
-(IBAction)popToRootViewController
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
This should result in calling the viewWillAppear method of the rootViewController and it is happening in most of the cases. However, occasionally viewDidLoad of rootViewController is called. I am not able to find the reason behind it. Does any one has any idea why viewDidLoad is called sometimes?
On iOS 5 and Earlier, the System May Unload Views When Memory Is Low:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html
viewDidLoad is called once when view controller's view is loaded first time.
viewWillAppear will be called after viewDidLoad method when view controller's view is loaded first time.
Now when ever u push or pop controller in navigationController, the visible controller's viewWillApper method will be called surely.
viewDidLoad, as the name implies, is called just after a view controller has loaded its view. If a view controller is no longer the frontmost controller, it may release its view to save memory (and it used to call viewWillUnload and viewDidUnload which are now deprecated in iOS 6). If this happens, when it comes to front again (or whenever something calls thecontroller.view), it will recreate the view (if is not Nib-based, it will call loadView), and then call viewDidLoad.
What is the difference between viewDidLoad and viewDidAppear? What kind of initialization or custom code goes into those functions?
e.g. presentModalViewController works only when present in viewDidAppear and not on viewDidLoad.
viewDidLoad is called exactly once, when the view controller is first loaded into memory. This is where you want to instantiate any instance variables and build any views that live for the entire lifecycle of this view controller. However, the view is usually not yet visible at this point.
viewDidAppear is called when the view is actually visible, and can be called multiple times during the lifecycle of a View Controller (for instance, when a Modal View Controller is dismissed and the view becomes visible again). This is where you want to perform any layout actions or do any drawing in the UI - for example, presenting a modal view controller. However, anything you do here should be repeatable. It's best not to retain things here, or else you'll get memory leaks if you don't release them when the view disappears.
See: https://developer.apple.com/documentation/uikit/uiviewcontroller
Simply put, you would want to create any controls or arrays in viewDidLoad, where as in viewDidAppear is where you would want to refresh those controls or arrays.
viewDidLoad is called once when the controller is created and viewDidAppear is called each time the view, well, DID appear. So say you have a modal view that you present, when that view is dismissed, viewDidAppear will be called, and viewDidLoad will not be called.
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.