I'm writing an app that has two sections like this:
The idea is that on the top half I'm creating a request, and on the bottom I'm displaying the results. As my UIViewController subclass grows and grows I thought it might be a good idea to split that into two UIViewControllers. (So what I'm trying to do is something like a UISplitViewController).
Although I know it's possible programmatically I'd like to put both UIViewControllers in the screen from the Interface Builder so I can manage autolayouts and design there. Dragging and dropping doesn't work there.
Is that possible? And more important - is that a good design or should I stick to the rule "One view controller for one screen"?
You can use a custom container view controller to achieve what you want. See:
WWDC 2011 #102 on UIViewController Containment (Apple developer ID required)
the containment section of the View Controller Programming Guide
the containment section of the UIViewController Reference document
In short, since iOS 5, you no longer have to have a single view controller. Just make sure you call the necessary containment related calls. And iOS 6 makes it easier to design the storyboards for custom containers with the "container view" object.
Apple have stated (WWDC 2012, Evolution of View Controllers, I think) that the "screenful" is no longer appropriate. A view controller should manage a self-contained chunk of functionality.
As of iOS5 you can compose an interface of multiple view controllers using view controller containment (addChildViewController: and so forth). As of iOS6 you can do this in interface builder using containers and containment segues.
You will of course need a view controller to hold your two child view controllers - this will hold a screenful of content, and will usually perform any coordination between the two children.
Related
I have a UIScrollView, contains 3 pages.
The first two are UITableView, the third one is a UICollectionView
So in one View Controller I will have to write delegate methods for all of them.
I googled a bit and found a solution like this (not tested yet),
Place each page in a separate UIViewController, implement the
corresponding delegate methods, and use UIViewController.view
attribute to build the scroll view
The UIViewController.view approach seems wrong to me, is that the normal way to do it?
Why are you trying to handle all the delegate calls in one view controller, can't you create separate view controller for each of them? If you are not separating now then it will be tougher for you to manage code later.
Absolutely using controller composition is a standard and respected solution to the Massive View Controller problem. Examine for instance the expositions at the StackViewController project which cleverly uses that concept to create forms in a UIStackView:
Composition over inheritance is a fundamental principle of object-oriented programming.
This principle has always been used in iOS view hierarchies, where more complex views are composed out of simpler ones (e.g. how a UIButton contains a UILabel and a UIImageView that render its content). However, there was no "official" way to compose view controllers until the introduction of view controller containment in iOS 5. It was possible to mimic behaviour like this prior to iOS 5, but handling the propagation of events between parent and child view controllers and transitions between child view controllers was difficult to get right, which are all problems that the view controller containment API solves.
In the same way that you can create complex layouts by composing multiple UIStackView instances, you can use the view controller containment API to compose multiple instances of StackViewController to create a hierarchy of view controllers where each content view is backed by a corresponding view controller that cleanly separates the responsibilities, instead of handling all of that at the view level (an anti-pattern, as mentioned earlier).
Also review 8 Patterns to Help You Destroy Massive View Controller, particularly
Standard Composition
View controllers can be composed using the View Controller Containment APIs introduced in iOS 5. If your view controller is composed of several logical units that could each be their own view controller, consider using Composition to break them apart. A practical application of this litmus test is a screen with multiple table views or collection views.
Which is, in fact, the exact situation that you have to practically apply it to!
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.
Referring to this question: addChildViewController alternative for iOS 4.3
My app requires support for 4.3 so I am unable to take the approach mentioned in the Apple guidelines for container programming - http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
Directly swapping the views of each ViewController works for me currently, however, when it comes to pushing a new View Controller, these 'children' do not have a reference to the container's self.navigationController so they are unable to push a new controller on the stack.
What is the best practice for being able to access the UINavigationController of the container class in iOS 4.3. It is a shame I am being forced to support 4.3 as the childViewController pattern outlined in the Apple documentation is exactly what I'd need otherwise!
Thanks,
Tim.
In iOS 4 and before, you are not supposed to create your own UIViewController hierarchy, so you have to manipulate views directly as subviews of a UIViewController's view.
However, if you do this correctly, there is still a UIViewController hierarchy and it does still work. There is no circumstance in which you "do not have a reference" to the UINavigationController. A view has a superview, which is another view. A UIViewController's view has a reference to the UIViewController (as its nextResponder). The UIViewController has a reference to its parent UINavigationController if there is one.
If that's not the case, you're doing something very wrong with view controllers.
Apple's documentation says this (for iOS 4 and before), and you should take it very seriously:
You should not use multiple custom view controllers to manage different portions of the same view hierarchy. ... If you want to divide a view hierarchy into multiple subareas and manage each one separately, use generic controller objects (custom objects descending from NSObject) instead of view controller objects to manage each subarea. Then use a single view controller object to manage the generic controller objects.
The rule in iOS 4 and later remains the same: do not use a UIViewController except as part of a legitimate UIViewController hierarchy, where the view controller's view is in exactly the same hierarchy, as in this diagram from my book:
Observe that the view controllers are parent and child and that their views are superview and subview. That is how to use a view controller hierarchy. If you're doing things in iOS 4 that would violate this, don't. Use views but not view controllers.
I have a UIViewController having two parts:
a UIView
a bar having multiple drop down menus arranged horizontally and having thumbnail images at the top
Because second part is little complex I've decided it to be a UIViewController but now I have some concerns:
Because I have drop down menu, menu will exceeds the bounds of the bar. How can I handle it?
Is it a good way to have a UIViewController inside a UIViewController?
How can I implement a drop down menu? As far I know IOS doesn't have drop down menus.
To use a controller within another controller, you employ a custom container view controller.
See Creating Custom Container View Controllers section of the View Controller Programming Guide for iOS.
Also see the appropriate Implementing a Container Controller section of the UIViewController Class Reference.
Also refer to the WWDC 2011 video, Implementing UIViewController Containment
In iOS 6, you can set up storyboards with container views that automatically employ embed segues, saving you from needing to explicitly call addChildViewController and the like, if you're using storyboards. Check out the "container view" object in Interface Builder. If you're going to be changing the child controller, you'll have to employ the API referred to in the above links, but for the configuration of the first child, you can set that up in Interface Builder in iOS 6.
In this case, setting up a controller containment could be the right way. The only limitation is that it works for iOS 5 and greater.
Here, what you have to do:
// add as child VC
[self addChildViewController:_barViewController];
// add it to container view, calls willMoveToParentViewController for us
[_containerView addSubview:_barViewController.view];
// notify it that move is done
[_barViewController didMoveToParentViewController:self];
Here, you can find additional info Containing ViewControllers. Obviosly Apple doc is your friend. In addition, if you search for "uiviewcontroller containment" you can find a lot of tuts out there.
If your app needs to target devices where iOS 5 is not the minimum, you should rely on a UIViewController and two different views.
About drop down menus, in my opinion they don't work so well with touch interfaces. There are some alternatives, for example an instance of the UISegmentedControl class. Here you can read Apple UI design guidelines about segmented controls: http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW1. If you explain a little bit more about your desired UI functionality we could offer you a better alternative from the user experience point of view.
If you insist with drop down menus, there are some third party control libraries available out there; for example: http://www.cocoacontrols.com/
I have a ViewController (with navigation) that needs to show 7 different content layouts. I want to keep the same background and nav, the only thing that needs to change is the central UIView.
If I have 7 different UIViews on the same xib/storyboard, can I hide the ones I'm not using or will that ding performance?
Using segues won't work either because they make a mess out of my custom navigation and animations.
Is there a better way to accomplish what I am trying to do? Thanks for suggestions
solution
My design is too custom for using view controller containment so I decided to mimic the idea with a custom UIViewController and two UIViews. It's not too bad and it works rather quickly.
You should look into using view controller containment, then you can load your views from separate nib files and still provide your custom navigation and animations from your container view controller.
Note: This is only really supported from iOS 5.
Generally, it's a good idea to unload views that aren't visible, however, if your views aren't using too much memory (and/or cpu time) hiding them when they're not in use should work fine.
View controller containment is probably what you should be doing if each view has its own unique functionality (i.e. view 1 is a map, view 2 shows some about text, view 3 is an image gallery). UITabBar might be useful, but it depends on your app.
The performance hit would depend on your views' contents. If you haven't done so already, invest some time into learning how to use Instruments (apple's diagnostic tool). Watching the video titled "Optimizing App Performance with Instruments" in the developer resources would be a good start.
My design is too custom for using view controller containment so I decided to mimic the idea with a custom UIViewController and two UIViews. It's not too bad and it works rather quickly.