Using Composition in iOS Architecture
I recently updated an old github project of mine that I created almost four years ago when I first started doing iOS development. It was an example of using container view controllers, in particular I was looking at transitioning between view controllers. In the project three child view controllers were contained in a single view controller and they rotated in a counter clockwise action periodically. This was an extremely simple and contrived example of view controller containment, just to help me understand how such a technique would work.
In revisiting view controller containment I found several recent blog posts and conference talks about using container view controllers in iOS to assist with the separation of concerns and prevent massive view controllers. People like Dave DeLong, John Sundell, and Paul Hudson. This made me think about my original implementation and how I approach building iOS apps now compared to when I first started developing apps.
View controller containment is well known to iOS developers as we use Apple’s containers all the time in the form of
UISplitViewController. Custom container view controllers have been around since iOS 5 and were implemented by Apple, in part, to prevent what they saw as abuse of the view hierarchy in apps. This occurs when the view from one view controller is “ripped” out and placed in another controller’s view hierarchy. This leads to many of the view controller appearance and rotation methods not being called, which in turn leads to unexpected behavior and a bad user experience. Apple’s talk can be found here: WWDC 2011 — Session 102. The solution is fairly straight forward as all
UIViewController classes have methods designed to add and remove child controllers. In fact there are several methods that need to be called when adding or removing a child view controller, which can be found in Apple’s View Controller Programming Guide for iOS.
There are also two appearance methods mentioned in Apple’s guide, which the parent (container) view controller could call to inform the child of pending appearance changes.
I created an example app to explore several ideas including view controller containment, composition, and pure code layout. The app contains two content view controllers; one is a simple user profile and the other story-like content. These are combined in a container view controller allowing you scroll the content vertically. If you had multiple view controllers being used your app, but then needed them to appear together in single screen, you can accomplish that using view controller containment. View controller containment helps to illustrate the concepts of composition and separation of concerns. Each view controller is a separate component with a single purpose (i.e. display a user profile). These components are then combined in a container view controller to create a more complex screen of content
UIViewController’s content views are laid out using code only, no
InterfaceBuilder and no
Auto Layout. I wanted to try something different in this example, so I opted to just lay the views out in code. This has some challenges in more complex view hierarchies, but it also has some advantages. The downside is there can be some complex math involved, as you need to calculate the position and size of each view manually. In this simple hierarchy, it is relatively easy, but in a more complex view, you might want to divide the screen into separate sections, build those views manually and then combine all the views together. Composition is one of the most powerful and important concepts in programming. On the plus side it means that layout changes are handled relatively easily, there are no constraints to add/remove or adjust. We override
viewWillLayoutSubviews and calculate the view’s frames. Whenever the device is rotated this method is called again and the views frames are adjusted accordingly, very nice.
In a real world app I would override
loadView and place all the view layout code in a custom view class that could be reused in other parts of the app as required. We will do that in a follow-up post, Container View Controllers Redux, where we will build a more complex and complete app, that includes additional concepts like
ProfileViewController contains a custom view to create a circular profile image. We see circular images in many apps today. Often the rounded image is created in each
UITableViewCell. However, we should be thinking about composition again, and designing reusable views that we can compose with other design elements. View controller containment is another great example of composition as we can use it to combine existing components isolating their individual complexity while building more complex features. We create a
ScrollingViewController class with a
scrollViewContent property to hold content view controllers. The entire contents can then be scrolled similar to a news feed in Facebook or Twitter.
ScrollingViewController class is used to contain the content of other
UIViewControllers. This is loosely drawn from a project that I was involved with at my work where a screen contained a profile view, a list of items for rent by the user and some controls (e.g. add new item). The profile view was displayed in many locations in the app, and should have been a separate component, designed and built once. Instead it was being recreated in each
UIViewController where it was being displayed.
loadView to replace the
UIViewController‘s main view with a
UIScrollView. The content views are then add to the
UIScrollView when the
UIViewController is initialized. The
layoutContentViews method is used to calculate the frame and position of each content view in the
PortraitViewController has a
preferredContentSize, since it does not encompass the entire screen. We use this property to adjust that view’s frame accordingly. Apple’s documentation describes this property as:
The value in this property is used primarily when displaying the view controller’s content in a popover but may also be used in other situations. Changing the value of this property while the view controller is being displayed in a popover animates the size change; however, the change is not animated if you specify a width or height of
preferredContentSize is updated each time the view lays out its subviews. We need to handle this change in the parent view controller. The important method in this case is
preferredContentSizeDidChange, according to Apple’s documentation this method
Notifies an interested controller that the preferred content size of one of its children changed
We override this method to update the content views of the
UIScrollView when the device is rotated. We set the
contentSize property to zero, and then call
layoutContentViews. This allows us to handle adjusting the content views on device rotation.
note: view controller containment may not be suitable for paginated data or infinite scrolling, since the view controllers are created up front not on demand like a
UITableView’s data source and reuse pool provide.
I hope you enjoyed this post. When building your apps try to actively incorporate composition in your design. Even small steps like just separating state properties into enums, making new types to hold method parameters, encapsulating networking code or moving view creation code into a separate class can go a long way to making your apps easier to understand and maintain.
The final project can be found here: ViewControllerContainment
If you are interested in any of the authors mentioned in this post you can follow them on Twitter at