iOS container view controller - Why does transitionFromViewController need the fromViewController? - ios

When using custom container view controller, I don't quite understand why the presenting view controller needs to specify the from, because being the container class, it should already know what's in the view hierarchy, no?
transitionFromViewController:toViewController:duration:options:animations:completion:

Container view controllers sometimes need to put the views of the contained controllers into specific subviews of their own view. (For example, a SplitViewController reimplementation might have left and right positioning views holding the master and detail controller views respectively.) Providing the fromViewController tells UIViewController where in the view hierarchy the new controller's view should be inserted, and also which specific view should be removed after the animation.
(contrary to another answer, the frames of the views aren't set for you at all. You do that, before the call, and in the animation block. The "Creating Custom Container View Controllers" system guide in the docs has a pretty good example.)
As it happens, actually using transitionFromViewController:... appears to be optional. You can manage your view hierarchy manually, with or without animations, and it works fine. I'm still forming my opinions, but I think I prefer to do it manually, to more easily handle cases where one of the VCs is nil.

This is done this way to allow you to have a view controller that has views with viewControllers in it. The from defines the originating view controller and gives the system the ability to position the animations appropriately.
Imaging you had a view with 4 views in it like tiles. the main view controller can consecutively call this on its "child" view controllers and with the from -> to specification, it won't make the assumption that the caller is the from viewController.

Related

Displaying instances of view controllers within Xcode without disturbing the current hierarchy

I am producing an application written in swift in Xcode, I have reached a point in development in which I need to be capable of instancing views. Ideally I would alter the hierarchy of my program to contain a 'Main' or 'Parent' view, the main view would be responsible for displaying the views given to it.
Instancing other view controllers and passing the instance to the main view would be my goal, however, i am inexperienced with Xcode and swift, I am unsure how to go about including a main view without altering my entire storyboard hierarchy.
Here is an image to give a visual description of the current structure.
The intention is to alter the hierarchy to include a main/ parent / master view, the view would be an empty display item which will display all views as and when needed, ideally I would keep the tab control method.
Mainly and most importantly the view will allow instances of view controllers to be displayed, closed and refreshed without impacting other aspects of the app.
Code seems irrelevant here as my views are handled by the storyboard. Something which would be really helpful to me here would be a brief explanation of how I can handle instancing with the new hierarchy.
Thank you in advance for any help regarding this matter
A view controller can serve as a custom parent view controller of one or more child view controllers, displaying their views in any desired manner within its own view's interface.
You can instantiate a view controller in any desired manner. If you wish to pluck a view controller instance out of the storyboard (because you have already designed its view there), call:
https://developer.apple.com/documentation/uikit/uistoryboard/1616214-instantiateviewcontroller
Your view controller in the storyboard will need to have a storyboard ID string so that you can identify it.
After you've plucked a view controller from the storyboard in that way, to display its view in your interface, you must do the following dance:
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621394-addchild
The parent captures the view controller's view and sticks it into the interface as a subview of the parent's own view.
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621405-didmove
The two view controllers now stand in a legal parent-child relationship and both view controllers will work properly. They can even refer to one another.
If you wish to remove the child view controller's view from the interface, you must reverse the dance:
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621381-willmove with a nil parameter
The parent removes the child view controller's view from the interface
The parent calls https://developer.apple.com/documentation/uikit/uiviewcontroller/1621425-removefromparent
Please read "Implementing a Container View Controller" on the main view controller docs page:
https://developer.apple.com/documentation/uikit/uiviewcontroller

Creating individual View Controllers for each view iOS?

This is a very simple simple question, and I believe the answer is “yes, it’s a best practice,” but I just wanted to ask the question.
When creating new views in your storyboard in Xcode, is it a good idea to create individual custom view controller files to handle each view specifically?
Meaning, if I create a new view called “login quiz,” should I create a “loginQuizViewController” that will handle all the code I write for that view?
Short answer:
Yes, each storyboard scene generally has a unique view controller class associated with it. A view controller has a root view, which in turn may have many subviews below that, and each subview might have further subviews. This collection of views is collectively known as the “view hierarchy”.
Long answer:
Yes, each storyboard “scene” has a view controller associated with it. And generally this view controller class is a particular UIViewController subclass which is unique to that particular Interface Builder scene. But you don’t necessarily have to have a view controller subclass and might, for example, use one of the existing classes (e.g. this is not uncommon for navigation controller scenes or tab bar controller scenes).
But if you have any custom logic associated with a particular storyboard scene, then, yes, you would generally have a unique view controller subclass for that particular scene.
Two minor clarifications:
You refer to the “quiz view”.
That’s fine for colloquial purposes, but for the sake of clarity, when we’re talking about everything for this quiz, it’s really a complex hierarchy of views, not just a one.
A single “quiz” scene will be associated with a unique view controller class, and the instance of that view controller class will have a single “root view” (identified with the view property), but that view will have a whole bunch of subviews (e.g. image views, buttons, labels, etc.), and some of those may have subviews of their own.
So one storyboard scene has its own unique view controller class, but is associated with a whole hierarchy of views.
We often think of a scene, and its associated view controller, as representing everything you might see at any given point in time, but it’s not always a one-to-one relationship. Sometimes what you see is composed of several storyboard scenes and their respective view controllers.
For example, if you are using a navigation controller, the navigation controller takes care of the navigation bar at the top, and your view controller might take care of everything under the navigation bar. What is visible on screen is composed of these two view controllers.
Or, in a more extreme example, we can define our own view controller containers. In the example below, when we present A, we can define the bottom half of the screen to be managed by a completely separate scene, B, which has its own view controller:
In this case, both A and B have their own IB scenes and their own respective view controllers. Achieve this by adding a “container view” to the A scene (the light blue UIView in the above screen snapshot).
But the main point is that what you see on screen may be captured by a single storyboard scene and view controller, or it might be composed of several.
A view controller can and probably will contain multiple views apart from the root view it has. That means that usually a view controller owns one ore more views that are subview's of it's own root view. Those views are often also controlled by the same view controller.
When coming from the storyboard most items you see are actually representations of view controllers (others are placeholders). "Login Quiz" sounds like a screen in conceptual terms, so (without knowing your details) it would probably make sense to create a LoginQuizViewController.

Relationship among window, rootviewcontroller, childviewcontroller, navigationcontroller in iOS

I haven't really seen any resource that gives a good and simple explanations on relationship among window, rootviewcontroller, childviewcontroller, navigationcontroller and how they piece together in iOS development. Anyone one knows how to put this in a easy-to-understand way or any online resource or book that does a good job in explaining it?
Per the documentation on UIWindow:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWindow_Class/
A UIWindow object provides the backdrop for your app’s user interface and provides important event-handling behaviors. Windows do not have any visual appearance of their own, but they are crucial to the presentation of your app’s views.
Xcode typically provides your application's main window, but you can add more if you need to.
From the documentation link you can see that UIWindow is actually a UIView
Enter your first view controller. Like providing a main window, when you start a new Project in Xcode the project template usually wires up your initial view controller, which as the name implies controls a view (UIView).
You could call this initial view controller your RootViewController but if you get a handle on the UIWindow you could just as easily swap out the current initial view controller's view for any other view controller view you like.
That probably doesn't help with hard and fast rules for things, but if I understand what you are asking, RootViewController is likely the initial view controller for you application. For example, if you are using Storyboards, Xcode typically makes Main.storyboard, you will see a gray arrow pointing to the UIViewController representation.
This is pointing to the Storyboards Initial View Controller. You can verify this from the Attributes Inspector. Select the view controller then select the attribute inspector:
So that's basically RootViewController. ChildViewController is just any other view controller that is a child of a view controller.
I assume what you are referring to is:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
You can read more about these methods here:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/
Under Implementing a Container View Controller
The quick gist of it is, A View Controller controls a view. View's can have subviews. Those subviews can come from other View Controllers. The methods outlined above pretty much just enable things like viewWillAppear, or viewWillDiappear to be called on the child view controller automatically when those methods are invoked on the parent view controller.
Per the docs:
By default, rotation and appearance callbacks are automatically forwarded to children. You may optionally override the shouldAutomaticallyForwardRotationMethods and shouldAutomaticallyForwardAppearanceMethods methods to take control of this behavior yourself.
a NavigationController is just like any other View Controller. It contains some special behavior for transitioning between views, but like other View Controllers it has a View (UIView) that it manages. A navigation controller could be your Initial View Controller / RootViewController just as any other View Controller can be, it all just depends on what you are trying to do. For example, a simple app that is just a list view, where you can tap an item and get a detail view could be constructed as:
1) Initial View Controller -> NavigationController
2) The NavigationController's first ViewController (Apple calls this a RootViewController) would then be a TableViewController.
3) Selecting a TableCell in the TableView (TableViewController manages a TableView) would then transition you to your Detail View Controller. The Navigation Controller knows how to do all that Sliding back and forth drama.
That's a pretty simplistic overview you can search the internet/youtube for more full featured tutorials outlining the same thing in more detail.
Example: https://www.raywenderlich.com/113388/storyboards-tutorial-in-ios-9-part-1
It's worth your time to do a few of these to get your bearings. Yes, it will likely cost you a few hours of your day. Take heart, everyone who ever started doing iOS development had to go though the same thing. =)

What are the benefits of using a UINavigationController over adding a view with addSubView?

Let's say I have two pages with two UIViewControllers, UIViewController1 & UIViewController2.
If I want to show a UIViewController2 on top of UIViewController1 I have three options:
using UINavigationController pushViewController.
using presentViewController.
addSubView : UIViewController1.view.addSubView(UIViewController2.view)
If I need to a transition between my views, I prefer the third option because it gives me much more control over the views.
Is there any difference between these three options in terms of performance?
Before iOS 6 you were not supposed to do option 3. View controllers were meant to control the entire screen. In iOS 6 Apple added support for parent and child view controllers. You could make another view controller your child and add it's content views to yours.
If you are going to use option 3 then that's what you need to do. If you don't you will have a variety of problems.
There is even support for parent/child view controller built into storyboards. You can add a container view to a view controller, and then control drag from the container view onto another view controller's scene. When you do that the system creates an "embed segue" that sets up the child view controller inside the container view and wires up the parent/child relationship for you.
Your first 2 options are for when you want the new view controller to replace, or at least cover, the first view controller. Option 3 is for when you want a region of your screen to be managed by another view controller.
Option 3 (using a child view controller) does mean that both view controllers will be active and in memory at the same time, so you can't release the covered view controller's data storage while it's inactive like you can in a push or modal presentation. However unless your view controllers hold and present huge data structures this isn't much of a concern. In both a push and a modal presentation the covered view controller sticks around in memory anyway, waiting to be uncovered. You have to take special steps in order to free any memory while a view controller's view is covered and then reallocate it when it is displayed again - something that is unusual.
Just for two view controllers, it will not create major difference. UINavigationController is mainly used to maintain navigation stack.
But as you are having just two view controllers, you can use alternate way also.
If you are looking for transitions with NavigationController, you can use UIView Transitions for customisation of push pop animation.
Please refer following links for UIView Transitions
https://www.objc.io/issues/12-animations/custom-container-view-controller-transitions/
http://www.raywenderlich.com/86521/how-to-make-a-view-controller-transition-animation-like-in-the-ping-app
https://www.captechconsulting.com/blogs/ios-7-tutorial-series-custom-navigation-transitions--more
http://applidium.github.io/ADTransitionController/

Why does presenting a modal UIViewController cause setNeedsLayout on the presenting controller?

It appears that presenting and dismissing a view controller both prompt the presenting view to layout its subviews and/or update its constraints. With a heavy view hierarchy, this is introducing performance issues. Again - this is the existing, currently displayed view. The modal being created and displayed is very light.
This occurs whether I use autolayout (as in my example project) or not.
I have built a demo project that approximates an app I am working on. There is a main parent controller with a horizontally scrolling UIScrollView. Multiple child controllers are added to the parent controller, and their views are added to the scrollview and arranged using NSLayoutConstraints. Each child view has one subview itself, a simple UIView, also arranged with a constraint.
In the navigation bar, there is a button to launch a modal. When presented, the parent controller makes a call to setNeedsLayout on each child view, multiple times. In my demo project, I am overriding setNeedsLayout to log when it is accessed. The same occurs when closing the modal. Open and close the modal a few times and observe the console.
I can see no reason why a new layout is needed, and with more complex views I am finding that hundreds of these calls are firing, with a noticeable performance impact.
Note that when the layout code from ChildView is omitted, setNeedsLayout is not called. I encourage you to comment out the constraints and see the difference in the logging.
Why is this happening? How can I prevent an unnecessary layout pass when presenting and dismissing a modal?
First of all, you are logging setNeedsLayout, which is just a flagging mechanism and does not really incur any work yet. Multiple calls to setNeedsLayout may only trigger a single layout. You should be logging -[UIView layoutSubviews] or -[UIViewController viewDidLayoutSubviews] instead, because these are where the actual heavy-lifting happen.
Second, layout-related methods are meant to be called repeatedly and rapidly during presentations because:
The window needs to rotate all its subviews to respect the presented view controller's preferred interface orientation.
Animations will need to know the initial and final states of your views.
When layouts happen on parent views for whatever reason, all their subviews (which may include views of your view controllers) will of course need to update their layouts too.
If you want to minimize the number of layout passes, you can try give up using presentViewController:animated: and instead use addChildViewController: and animate just the necessary views manually. But even then, you may still trigger the parent controller's layout anyway.
You are doing a very, very, very odd thing: you're maintaining a custom parent view controller with 10 child view controllers all of whose views are in the interface simultaneously. View controllers are not designed for that sort of thing. It is this that is triggering the multiple layoutSubviews calls that you are seeing. It is fine to have multiple child view controllers, but their views should not all be in the hierarchy - especially in your case, where only one such child view is actually visible.
In fact, the interface that you've constructed - a paging scroll view, each of whose "pages" is a view managed by a view controller - is already implemented for you by UIPageViewController, which is far more efficient, as it only actually maintains at most three view controllers at a time: the view controller managing the visible view within the scroll view, and the view controllers managing the views to its right and left. It is also wonderfully convenient and easy to use.
So either:
You should use UIPageViewController, or
You should imitate what UIPageViewController does, removing view controllers' views (and perhaps even releasing the view controllers) when they have scrolled out of sight - as we had to do in the days before UIPageViewController existed - see the Advanced Scroll View Techniques video from WWDC 2011. My Latin "flashcard" app worked this way before UIPageViewController came along; it has thousands of vocabulary cards, each of which is managed by a view controller, but only a maximum of three card view controllers ever exist at any one moment.
(By the way, you should also not be using your own self.childControllers mutable array, as this list is already maintained for you as self.childViewControllers.)
I think layoutSubviews is getting called because the presenting controller's view changes superviews while animating out of the screen once hidden by the presented view.
If you want to avid skip layoutSubviews when the frame hasn't changed, just save a reference to the last frame and if equal return without doing anything. Also there is no need to call setNeedslayout on subviews as the system will trigger it automatically if you resize them.
Anyway, your main problem is your approach:
Only use view controllers if you're going to use them as such (inside a tab bar controller, pushed to a navigation controller, as a window's rootController, presented modally, etc.). If you want to manually add views do not use view controllers and just use custom views! This is a very common error and you can see more details here.
Load views and objects lazily and reuse them. For instance you should only load 1~3 pages of contents and load new ones only when the user scrolls to them. When loading a new one remove one of the old views, or better yet reuse it.
You can separate the logic not only with controllers but also with custom views. Some reasons why you should not use controllers in your particular case:
Controllers won't get retained by a container controller or window as you're manually adding their views.
Controllers won't get orientation, memory, viewDidAppear, etc., events. Again because you're not using them as proper view controllers.
If you properly implemented a custom container controller (which is a lot of work to do properly), then you could use controllers. Otherwise stick to custom views.

Resources