Once you’ve designed your class’s behavior and determined many aspects of its public API, you are ready to start implementing the container. The goal of implementing a container is to be able to add another view controller’s view (and associated view hierarchy) as a subtree in your container’s view hierarchy. The child remains responsible for its own view hierarchy, save for where the container decides to place it onscreen. When you add the child’s view, you need to ensure that events continue to be distributed to both view controllers. You do this by explicitly associating the new view controller as a child of the container.
The
UIViewController class provides methods that a container view controller uses to manage the relationship between itself and its children. The complete list of methods and properties is in the reference; see “Managing Child View Controllers in a Custom Container” in UIViewController Class ReferenceAdding and Removing a Child
Listing 14-1 shows a typical implementation that adds a view controller as a child of another view controller. Each numbered step in the listing is described in more detail following the listing.
Listing 14-1 Adding another view controller’s view to the container’s view hierarchy
- (void) displayContentController: (UIViewController*) content; |
{
|
[self addChildViewController:content]; // 1 |
content.view.frame = [self frameForContentController]; // 2 |
[self.view addSubview:self.currentClientView]; |
[content didMoveToParentViewController:self]; // 3 |
} |
Here’s what the code does:
- It calls the container’s
addChildViewController:method to add the child. Calling theaddChildViewController:method also calls the child’swillMoveToParentViewController:method automatically. - It accesses the child’s
viewproperty to retrieve the view and adds it to its own view hierarchy. The container sets the child’s size and position before adding the view; containers always choose where the child’s content appears. Although this example does this by explicitly setting the frame, you could also use layout constraints to determine the view’s position. - It explicitly calls the child’s
didMoveToParentViewController:method to signal that the operation is complete.
Eventually, you want to be able to remove the child’s view from the view hierarchy. In this case, shown in Listing 14-2, you perform the steps in reverse.
Listing 14-2 Removing another view controller’s view to the container’s view hierarchy
- (void) hideContentController: (UIViewController*) content |
{
|
[content willMoveToParentViewController:nil]; // 1 |
[content.view removeFromSuperview]; // 2 |
[content removeFromParentViewController]; // 3 |
} |
Here’s what this code does:
- Calls the child’s
willMoveToParentViewController:method with a parameter ofnilto tell the child that it is being removed. - Cleans up the view hierarchy.
- Calls the child’s
removeFromParentViewControllermethod to remove it from the container. Calling theremoveFromParentViewControllermethod automatically calls the child’sdidMoveToParentViewController:method.
For a container with essentially static content, adding and removing view controllers is as simple as that. Whenever you want to add a new view, add the new view controller as a child first. After the view is removed, remove the child from the container. However, sometimes you want to animate a new child onto the screen while simultaneously removing another child. Listing 14-3 shows an example of how to do this.
Listing 14-3 Transitioning between two view controllers
- (void) cycleFromViewController: (UIViewController*) oldC |
toViewController: (UIViewController*) newC |
{
|
[oldC willMoveToParentViewController:nil]; // 1 |
[self addChildViewController:newC]; |
newC.view.frame = [self newViewStartFrame]; // 2 |
CGRect endFrame = [self oldViewEndFrame]; |
[self transitionFromViewController: oldC toViewController: newC // 3 |
duration: 0.25 options:0 |
animations:^{
|
newC.view.frame = oldC.view.frame; // 4 |
oldC.view.frame = endFrame; |
} |
completion:^(BOOL finished) {
|
[oldC removeFromParentViewController]; // 5 |
[newC didMoveToParentViewController:self]; |
}]; |
} |
Here’s what this code does:
- Starts both view controller transitions.
- Calculates two new frame positions used to perform the transition animation.
- Calls the
transitionFromViewController:toViewController:duration:options:animations:completion:method to perform the swap. This method automatically adds the new view, performs the animation, and then removes the old view. - The animation step to perform to get the views swapped.
- When the transition completes, the view hierarchy is in its final state, so it finishes the operation by sending the final two notifications.
Customizing Appearance and Rotation Callback Behavior
Once you add a child to a container, the container automatically forwards rotation and appearance callbacks to the child view controllers as soon as an event occurs that requires the message to be forwarded. This is normally the behavior you want, because it ensures that all events are properly sent. However, sometimes the default behavior may send those events in an order that doesn’t make sense for your container. For example, if multiple children are simultaneously changing their view state, you may want to consolidate the changes so that the appearance callbacks all happen at the same time in a more logical order. To do this, you modify your container class to take over responsibility for appearance or rotation callbacks.
To take over control of appearance callbacks, you override the
shouldAutomaticallyForwardAppearanceMethods method to return NO. Listing 14-4shows the necessary code.
Listing 14-4 Disabling automatic appearance forwarding
- (BOOL) shouldAutomaticallyForwardAppearanceMethods |
{
|
return NO; |
} |
To actually inform the child view controller that an appearance transition is occurring, you call the child’s
beginAppearanceTransition:animated: andendAppearanceTransition methods.
If you take over sending these messages, you are also responsible for forwarding them to children when your container view controller appears and disappears. For example, if your container has a single child referenced by a
child property, your container would forward these messages to the child, as shown in Listing 14-5.
Listing 14-5 Forwarding appearance messages when the container appears or disappears
-(void) viewWillAppear:(BOOL)animated |
{
|
[self.child beginAppearanceTransition: YES animated: animated]; |
} |
-(void) viewDidAppear:(BOOL)animated |
{
|
[self.child endAppearanceTransition]; |
} |
-(void) viewWillDisappear:(BOOL)animated |
{
|
[self.child beginAppearanceTransition: NO animated: animated]; |
} |
-(void) viewDidDisappear:(BOOL)animated |
{
|
[self.child endAppearanceTransition]; |
} |
Forwarding rotation events works almost identically and can be done independently of forwarding appearance messages. First, you override the
shouldAutomaticallyForwardRotationMethods method to return NO. Then, at times appropriate to your container, you call the following methods:Practical Suggestions for Building a Container View Controller
Designing, developing, and testing a new container view controller takes time. Although the individual behaviors are straightforward, the controller as a whole can be quite complex. Consider some of the following guidance when implementing your own container classes:
- Design the view controller first as a content view controller, using regular views owned by the container. This allows you to focus on getting layout and animation transitions correct without simultaneously needing to manage parent-child relationships.
- Never access any view other than the top-level view of the child view controller. Similarly, children should have only a minimal knowledge of what the parent is doing with the view; do not expose unnecessary details to the child.
- If the container needs the child to declare methods or properties, it should define a protocol to enforce this:
@protocol MyContentContainerProtocol <NSObject>
...
@end
- (void) displayContentController: (UIViewController<MyContentContainerProtocol>*) content;
コメント
コメントを投稿