I'm following an exercise from a book and we're building out the view controllers programmatically and creating unit tests. The book also says this, and from previous experience with reading Apple reference docs, Apple references always say to not invoke viewDidLoad, viewWillAppear, viewDidDisappear, etc directly. I'm curious to know why this is and what happens if we do?
viewDidLoad, viewWillAppear, viewDidDisappear and so on are the runtime's way of letting you know that certain important things are happening:
viewDidLoad, the view controller has just obtained its view
viewWillAppear, the view controller's view is about to be placed into the interface
viewDidDisappear, the view controller's view is about to be removed from the interface
These are events that the runtime is responsible for, and it sends you messages to let you know they are happening so that you can respond, if you wish, at the appropriate moment.
It would make no sense for you to call them, because you do not know when these things are happening (except insofar as the runtime calls them)! If you called them at some arbitrary moment, you would be lying, e.g. saying that the view has just loaded when in fact it has not just loaded, and so you would break your own code or worse.
Related
After implementing state restoration in my app I am having difficulty with my view controllers.
My issue is that in viewDidLoad of the view controller I am attempting to restore the self.naigationController property, which is nil.
I have set breakpoints in the viewDidLoad method of each view controller that comes before the one I am trying to restore. In the first view controller, the navigation controller is found. In every one after that, including the one I am trying to restore, the navigation controller is nil.
This is causing bugs where I cannot set the navigation bar visibility, views are misplaced, etc.
Does anyone have an idea why this might be?
Try moving the code to viewWillAppear.
Unlike viewDidLoad, viewWillAppear can get called multiple times, so take care to make sure your code is idempotent. That is, make sure the results are what you expect if the method is called repeatedly. For example, make sure you don't initialize a new subview if the view has already been added.
The navigationController property searches up the parentViewControllers to find one of class UINavigationController. Only view controllers loaded from the storyboard can find their navigation controller at the viewDidLoad time (and in awakeFromNib). It is likely you are instantiating a new instance of the view controller during the restore process rather than using the one that was created by the storyboard. The solution is either to help the restore process find the existing view controller if it has changed paths e.g. because of split controller orientation change (via viewControllerForRestorationPathComponents in your app delegate) although that comes with its own set of issues, or make your view controller work by not requiring the nav controller in the viewDidLoad, e.g. put it in the viewWillAppear as others have suggested.
I was left with continuing the code of a senior developer, where I came across a coding pattern that was not only bizarre, but got me curious about a lot of things. The pattern, however, that I spoke about had something like this:
There is a UIViewController the view of which has an instance of extended UIView attached to it as a subview.
This custom UIView class has a reference of the above-stated UIViewController.
There are a series of methods defined within the UIViewController that are responsible for handling events at the UIView.
Since this UIViewController exists as a reference, our custom view calls those event-handling methods through this reference!
In such a system of code, what are the memory implications? How is this any different from the delegate pattern? Under what circumstances using this sort of coding okay?
While this pattern is a little curious, I would hesitate to condemn this without more information about what this child view is doing and what it needs to inform the view controller about. There is, admittedly, a faint code smell here, and if I were to hazard a guess, I'd bet that this view is likely doing stuff that one would now generally put in a view controller.
Often, when adding a subview that has any significant complexity (or is likely to be reused in different views), one would consider an iOS 5 feature, "view controller containment" (see the Creating Custom Container View Controllers section of the View Controller Programming Guide or WWDC 2011 video Implementing UIViewController Containment).
If using storyboards, you can achieve much of this using the special "Container View" control introduced with iOS 6, found in Interface Builder's "Object Library" (in the bottom of the right panel in the standard Xcode layout). If doing this programmatically, just make sure to call the appropriate methods listed in the "Managing Child View Controllers in a Custom Container" section of the UIViewController Class Reference.
When using view controller containment, you have a parent view controller (the main view controller) and the child view controller (the view controller that is managing the subview). And in this scenario, it's very common to design a custom protocol by which a child view controller notifies its parent view controller of particular events. But rather than adding your own custom delegate property, you can use the built-in parentViewController property which is automatically populated when you adopt the above "view controller containment" pattern.
Having said all of this, I might let practical concerns drive whether the existing code base needs to be refactored or not. Perhaps the code predates iOS 5, but is a solid implementation of what we might have done back in the day. Bottom line, if it works, is otherwise well written, and has the delineation of responsibilities clearly defined, you might decide to just leave well enough alone. And if it's a little ambiguous (as the absence of a discussion of a protocol might suggest), perhaps just start by introducing a formal protocol between the child view and the view controller to make the interface explicit. Whether a more radical refactoring of the code (to use something like view controller containment) is called for is hard for us to advise on the basis of the limited information provided thus far.
I think this is a really a question about the difference in the run time loop when running the app target vs running a unit test for for my view controller.
I know that reloadData is being called but the delegate methods are never called, even the method to ask in the first place how many rows there are is never called in unit test but it does when running as a application.
What am i missing in the wide world of iOS?
EDIT
John was correct but for me the real issue was understanding that view controller's view is not instantiated until the views will actually be painted in the window. Since i was loading the view from a xib this requires that i actually call loadView (simple i know) from the unit test. Since objective c does not complain when you send a message to nil it never complained when i sent reloadData on a table that didn't exist
What I do is mock the table view controller, and ensure that reloadData is called on it.
In separate tests, I confirm what happens when the delegate methods are called.
There's no need to test that Apple will call the delegate methods.
I want to present view controllers as simple as with native presentViewController:animated:completion:, dismissViewControllerAnimated:completion: methods, but use custom animations for this.
The common way to do this is to perform custom animation (using view screenshots instead of view itself) and call presentViewController:vc animated:NO completion:… after.
But in this case view controller life-cycle messages are sent not in time. viewWillApper: and viewDidAppear: sent together instead of normal way: the first one just before animation and the last — after. Also they sent with animated parameter set to NO.
The other bad thing is that screenshot of a view (for animation) captured before viewWillApper: called. So it can be some outdated, and this will cause flicking at the end of animation.
I've searched lot of related projects on github/cocoapods (SO answers too), but did not find any with the right life-cycle of presented view controller. There are sometimes even worse solutions like simple views changing without any calls to life-cycle methods.
Possible solutions I know:
iOS7 — I can't, I need it in iOS6 too
Use transition between contained view controllers — Not as simple and reusable as "modal presenting"
Use tricky animations like here — my animation can't be implemented in similar way
While writing this question I thought about custom segue with overridden perform: method. But looks like it's not easy too. At least I don't know how to use it without storyboard and how to do dismiss in easy way.
Do you know how to present view controller with custom animation in iOS6 and make it's life-cycle happy?
Update: finally I've decided to use container view controller. It has ability to customize transitions of it's children and preserve VC's life-cycle. The bad side: it's not as easy as custom transitions in iOS7. It needs container, making xib some harder. And needs additional code to support.
I've been learning coredata by making a lot of simple test apps based on the xcode Navigation controller template with "use coredata" checked.
The awakeFromNib method in the App delegate has been a source of problems for me, because I'm adding other views to the controller and changing the load sequence, so that RootViewController may be a second or third choice.
I've figured out what awakeFromNib is doing, and I've removed it so the app delegate is no longer tied to any particular view. (So when I do want to load RootViewController, I'll load it as a regular view, and use its own viewDidLoad to initialize the managedObjectContext for the view).
My question: are there performance gains or other benefits by using awakeFromNIb in the AppDelegate? or is it just another way of doing the same thing as I'm doing from the viewDidLoad method?
All the methods fire at different times and different circumstances.
awakeFromNib is called when the nib file associated with a class is loaded from disk. Any class that can own a nib can use it. viewDidLoad is used only by view controllers. It is usually called when loading from nib as well but it can also be called by a view created in memory (very rare circumstance.)
In either case, you only put functionality in either that you only want to run once when the instance is first loaded. E.g. a common nubie mistake is to put code in viewDidLoad that needs to run every time the view appears. Say as with master view that opens a detail view and then reappears when the detail view is dismissed. If the code for the master view is in viewDidLoad it will only run the first time the master view is loaded but not any of the subsequent times the master view disappears and reappears.
You generally don't initialize any other views or do much of anything in the app delegate's awake from nib. That is usually performed in applicationDidFinishLaunching.