实现一个容器控制器[官翻]

版权声明:本文为博主原创文章,转载请注明文章出处。 https://blog.csdn.net/qfeung/article/details/88674997

Implementing a Container View Controller1

Container view controllers are a way to combine the content from multiple view controllers into a single user interface. Container view controllers are most often used to facilitate navigation and to create new user interface types based on existing content. Examples of container view controllers in UIKit include UINavigationController, UITabBarController, and UISplitViewController, all of which facilitate navigation between different parts of your user interface.

有一种可以将多个控制器的内容组合成一个单独的用户界面的方式, 叫做Container view controllers(后文简称为containerVc). containerVc常用于方便导航和创建新的界面类型(基于已经存在的内容). UIKit框架中的containerVc有UINavigationController, UITabBarControllerUISplitViewController, 以上三者的存在都是为了促成不同界面之间的导航.

设计一个自定义的containerVc

In almost every way, a container view controller is like any other content view controller in that it manages a root view and some content. The difference is that a container view controller gets part of its content from other view controllers. The content it gets is limited to the other view controllers’ views, which it embeds inside its own view hierarchy. The container view controller sets the size and position of any embedded views, but the original view controllers still manage the content inside those views.

在绝大多数方面, containerVc和其它普通的内容控制器是一样的, 都管理一个根视图和其它内容. 不同点在于containerVc的部分内容是来自于其它控制器. 其获取的内容被限制在其它控制器的视图中, 而这些其它控制器的视图又被嵌入containerVc自己的视图层级中. containerVc设定了任何嵌入其中的视图的尺寸和位置, 但是原始控制器依旧管理着自己的视图内容.

When designing your own container view controllers, always understand the relationships between the container and contained view controllers. The relationships of the view controllers can help inform how their content should appear onscreen and how your container manages them internally. During the design process, ask yourself the following questions:

  • What is the role of the container and what role do its children play?

  • How many children are displayed simultaneously?

  • What is the relationship (if any) between sibling view controllers?

  • How are child view controllers added to or removed from the container?

  • Can the size or position of the children change? Under what conditions do those changes occur?

  • Does the container provide any decorative or navigation-related views of its own?

  • What kind of communication is required between the container and its children? Does the container need to report specific events to its children other than the standard ones defined by the UIViewController class?

  • Can the appearance of the container be configured in different ways? If so, how?

在设计自己的容器视图控制器时,始终要了解容器和包含的视图控制器之间的关系。 此关系可以帮助告知其内容应如何显示在屏幕上以及容器如何在内部管理它们。 在设计过程中,请问自己以下问题:

  • 容器的角色是什么? children又扮演了怎样的角色?
  • 同时会展示多少个 children ?
  • 如何在容器中添加或删除子视图控制器?
  • 子控制器的尺寸和位置会改变吗? 在什么情况下改变?
  • 容器是否提供自己的装饰或导航相关视图?
  • 容器与其子容器之间需要什么样的通信? 除了UIViewController类定义的标准事件之外,容器是否需要向其子节点报告特定事件?
  • 容器的外观可以以不同的方式配置吗? 如果是这样,怎么样?

The implementation of a container view controller is relatively straightforward after you have defined the roles of the various objects. The only requirement from UIKit is that you establish a formal parent-child relationship between the container view controller and any child view controllers. The parent-child relationship ensures that the children receive any relevant system messages. Apart from that, most of the real work happens during the layout and management of the contained views, which is different for each container. You can place views anywhere in your container’s content area and size those views however you want. You can also add custom views to the view hierarchy to provide decoration or to aid in navigation.

在定义了各种对象的角色后,容器视图控制器的实现相对简单。 UIKit的唯一要求是在容器视图控制器和任何子视图控制器之间建立正式的父子关系。 父子关系确保孩子们收到任何相关的系统消息。 除此之外,大多数实际工作发生在所包含视图的布局和管理期间,每个容器都有所不同。 您可以将视图放置在容器的内容区域中的任何位置,并根据需要调整这些视图的大小。 您还可以向视图层次结构添加自定义视图以提供装饰或辅助导航。

实例: 导航控制器(Navigation Controller)

A UINavigationController object supports navigation through a hierarchical data set. A navigation interface presents one child view controller at a time. A navigation bar at the top of the interface displays the current position in the data hierarchy and displays a back button to move back one level. Navigation down into the data hierarchy is left to the child view controller and can involve the use of tables or buttons.

UINavigationController对象支持通过分层数据集导航。 导航界面一次呈现一个子视图控制器。 界面顶部的导航栏显示数据层次结构中的当前位置,并显示后退按钮以向后移动一个级别。 向下导航到数据层次结构留给子视图控制器,并且可以涉及使用表或按钮2

Navigation between view controllers is managed jointly by the navigation controller and its children. When the user interacts with a button or table row of a child view controller, the child asks the navigation controller to push a new view controller into view. The child handles the configuration of the new view controller’s contents, but the navigation controller manages the transition animations. The navigation controller also manages the navigation bar, which displays a back button for dismissing the topmost view controller.

Vc3之间的导航由导航控制器及其子控制器共同管理。当用户与子视图控制器的按钮或表行交互时, 子Vc请求导航控制器将新的Vc推入视图。 新的子Vc处理自己内容的配置,但导航控制器管理转换动画。导航控制器还管理导航栏,该导航栏显示用于关闭最顶层视图控制器的后退按钮。

Figure 5-1 shows the structure of a navigation controller and its views. Most of the content area is filled by the topmost child view controller and only a small portion is occupied by the navigation bar.

图5-1显示了导航控制器的结构及其视图。 大多数内容区域由最顶层的子视图控制器填充,并且导航栏仅占用一小部分。

Figure 5-1 Structure of a navigation interface

图 5-1 导航界面的结构
在这里插入图片描述

In both compact and regular environments, a navigation controller displays only one child view controller at a time. The navigation controller resizes its child to fit the available space.

在紧凑和常规环境中,导航控制器一次只显示一个子视图控制器。 导航控制器调整其子Vc(其实是子Vc的根视图)的大小以适应可用空间。

示例: 拆分视图控制器(Split View Controller)

A UISplitViewController object displays the content of two view controllers in a master-detail arrangement. In this arrangement, the content of one view controller (the master) determines what details are displayed by the other view controller. The visibility of the two view controllers is configurable but is also governed by the current environment. In a regularly horizontal environment, the split view controller can show both child view controllers side-by-side or it can hide the master and display it as needed. In a compact environment, the split view controller displays only one view controller at a time.

UISplitViewController对象以主从细节排列显示两个视图控制器的内容。 在这种布置中,一个视图控制器(主Vc)的内容决定了另一个视图控制器显示什么细节。 两个视图控制器的可见性是可配置的,但也受当前环境的控制。 在常规水平环境中,分割视图控制器可以并排显示两个子视图控制器,也可以隐藏主视图并根据需要显示它。 在紧凑的环境中,拆分视图控制器一次只显示一个视图控制器。

Figure 5-2 shows the structure of a split view interface and its views in a regularly horizontal environment. The split view controller itself has only its container view by default. In this example, the two child views are displayed side-by-side. The size of the child views is configurable, as is the visibility of the master view.

图 5-2 显示了在常规水平环境中拆分视图界面及其视图的结构。 拆分视图控制器本身默认只有其容器视图。 在此示例中,两个子视图并排显示。 子视图的大小是可配置的,主视图的可见性也是如此。

Figure 5-2A split view interface

图 5-2A 拆分视图界面
在这里插入图片描述

Configuring a Container in Interface Builder

To create a parent-child container relationship at design time, add a container view object to your storyboard scene, as shown in Figure 5-3. A container view object is a placeholder object that represents the contents of a child view controller. Use that view to size and position the child’s root view in relation to the other views in the container.

要在设计时创建父子容器关系,请在故事板场景(storyboard scene)中添加容器视图对象,如图5-3所示。 容器视图对象是占位符对象,表示子视图控制器的内容。 使用该视图可以根据容器中的其他视图调整该容器视图对应的子Vc的根视图的大小和位置。

Figure 5-3Adding a container view in Interface Builder

图 5-3 在Interface Builder中添加容器视图

在这里插入图片描述

When you load a view controller with one or more container views, Interface Builder also loads the child view controllers associated with those views. The children must be instantiated at the same time as the parent so that the appropriate parent-child relationships can be created.

使用一个或多个容器视图加载视图控制器时,Interface Builder还会加载与这些视图关联的子视图控制器。 必须在父项的同时实例化子项,以便可以创建适当的父子关系。

If you do not use Interface Builder to set up your parent-child container relationships, you must create those relationships programmatically by adding each child to the container view controller, as described in Adding a Child View Controller to Your Content.

如果不使用Interface Builder来设置父子容器关系,则必须通过将每个子项添加到容器视图控制器来以编程方式创建这些关系,如向子内容添加子视图控制器中所述。

Implementing a Custom Container View Controller

To implement a container view controller, you must establish relationships between your view controller and its child view controllers. Establishing these parent-child relationships is required before you try to manage the views of any child view controllers. Doing so lets UIKit know that your view controller is managing the size and position of the children. You can create these relationships in Interface Builder or create them programmatically. When creating parent-child relationships programmatically, you explicitly add and remove child view controllers as part of your view controller setup.

要实现containerVc,必须在视图控制器及其子视图控制器之间建立关系。 在尝试管理任何子视图控制器的视图之前,需要建立这些父子关系。 这样做让UIKit知道你的视图控制器正在管理子Vc的大小和位置。 您可以在Interface Builder中创建这些关系,也可以以编程方式创建它们。 以编程方式创建父子关系时,您可以在视图控制器设置中显式添加和删除子视图控制器。

Adding a Child View Controller to Your Content

To incorporate a child view controller into your content programmatically, create a parent-child relationship between the relevant view controllers by doing the following:

  1. Call the addChildViewController: method of your container view controller.
    This method tells UIKit that your container view controller is now managing the view of the child view controller.
  2. Add the child’s root view to your container’s view hierarchy.
    Always remember to set the size and position of the child’s frame as part of this process.
  3. Add any constraints for managing the size and position of the child’s root view.
  4. Call the didMoveToParentViewController: method of the child view controller.

要以编程方式将子视图控制器合并到您的内容中,请通过执行以下操作在相关视图控制器之间创建父子关系:

  1. 调用containerVc的addChildViewController:方法.
    这个方法会告诉 UIKit 你的containerVc现在正在管理子Vc的视图.
  2. 将子Vc的根视图添加到containerVc的视图层级中, 在此过程中始终要记得去设置子视图的大小和位置.
  3. 添加任何约束来管理子视图的大小和位置.
  4. 调用子Vc的didMoveToParentViewController:方法.

Listing 5-1 shows how a container embeds a child view controller in its container. After establishing the parent-child relationship, the container sets the frame of its child and adds the child’s view to its own view hierarchy. Setting the frame size of the child’s view is important and ensures that the view shows up correctly in your container. After adding the view, the container calls the child’s didMoveToParentViewController: method to give the child view controller a chance to respond to the change in view ownership.

清单5-1显示了容器如何在其容器中嵌入子视图控制器。 在建立父子关系之后,容器设置其子框架并将子视图添加到其自己的视图层次结构中。 设置子视图的帧大小很重要,并确保视图在容器中正确显示。 添加视图后,容器调用子进程的didMoveToParentViewController:方法,以便为子视图控制器提供响应视图所有权更改的机会。

Listing 5-1Adding a child view controller to a container

清单5-1将子视图控制器添加到容器中

- (void) displayContentController: (UIViewController *) content {
   [self addChildViewController:content];
   content.view.frame = [self frameForContentController];
   [self.view addSubview:self.currentClientView];
   [content didMoveToParentViewController:self];
}

In the preceding example, notice that you call only the didMoveToParentViewController: method of the child. That is because the addChildViewController: method calls the child’s willMoveToParentViewController: method for you. The reason that you must call the didMoveToParentViewController: method yourself is that the method cannot be called until after you embed the child’s view into your container’s view hierarchy.

在前面的示例中,请注意您只调用子项的didMoveToParentViewController:方法。 这是因为addChildViewController:方法为您调用子的willMoveToParentViewController:方法。 您必须自己调用didMoveToParentViewController:方法的原因是,在将子视图嵌入到容器的视图层次结构中之前,无法调用该方法。

When using Auto Layout, set up constraints between the container and child after adding the child to the container’s view hierarchy. Your constraints should affect the size and position of only the child’s root view. Do not alter the contents of the root view or any other views in the child’s view hierarchy.

使用“自动布局”时,在将子项添加到容器的视图层次结构后,在容器和子项之间设置约束。 您的约束应该仅影响子视图的大小和位置。 请勿更改子视图层次结构中根视图或任何其他视图的内容。

Removing a Child View Controller

To remove a child view controller from your content, remove the parent-child relationship between the view controllers by doing the following:

  1. Call the child’s willMoveToParentViewController: method with the value nil.
  2. Remove any constraints that you configured with the child’s root view.
  3. Remove the child’s root view from your container’s view hierarchy.
  4. Call the child’s removeFromParentViewController method to finalize the end of the parent-child relationship.

按照以下步骤去移除一个子Vc从你的 content, 移除Vc之间的父子关系:
1. 使用nil来调用子Vc的willMoveToParentViewController:.
2. 移除任何你之前配置的子Vc的根视图的constraint.
3. 从容器视图的视图层级中移除子Vc的根视图.
4. 调用子Vc的removeFromParentViewController来终结父子控制器的关系.

Removing a child view controller permanently severs the relationship between parent and child. Remove a child view controller only when you no longer need to refer to it. For example, a navigation controller does not remove its current child view controllers when a new one is pushed onto the navigation stack. It removes them only when they are popped off the stack.
删除子视图控制器会永久性地切断父级和子级之间的关系。 仅在不再需要引用子视图控制器时才删除子视图控制器。 例如,当将新的控制器推入导航堆栈时,导航控制器不会移除其当前的子视图控制器。 只有当它们从堆栈弹出时才会删除它们。

Listing 5-2 shows you how to remove a child view controller from its container. Calling the willMoveToParentViewController: method with the value nil gives the child view controller an opportunity to prepare for the change. The removeFromParentViewController method also calls the child’s didMoveToParentViewController: method, passing that method a value of nil. Setting the parent view controller to nil finalizes the removal of the child’s view from your container.
清单5-2显示了如何从容器中删除子视图控制器。 使用值nil调用willMoveToParentViewController:方法可以为子视图控制器提供准备更改的机会。 removeFromParentViewController方法还调用子的didMoveToParentViewController:方法,将该方法的值传递给nil。 将父视图控制器设置为nil,最终确定从容器中删除子视图。

Listing 5-2Removing a child view controller from a container

清单 5-2 从容器中删除子视图控制器

- (void) hideContentController: (UIViewController*) content {
   [content willMoveToParentViewController:nil];
   [content.view removeFromSuperview];
   [content removeFromParentViewController];
}

Transitioning Between Child View Controllers

When you want to animate the replacement of one child view controller with another, incorporate the addition and removal of the child view controllers into the transition animation process. Before the animations, make sure both child view controllers are part of your content but let the current child know that it is about to go away. During your animations, move the new child’s view into position and remove the old child’s view. At the completion of the animation, complete the removal of the child view controller.

如果要为一个子视图控制器替换另一个子视图控制器设置动画,请将子视图控制器的添加和删除合并到过渡动画过程中。 在动画之前,请确保两个子视图控制器都是您的内容的一部分,但让当前的孩子知道它即将消失。 在动画期间,将新孩子的视图移动到位并删除旧孩子的视图。 完成动画后,完成子视图控制器的删除。

Listing 5-3 shows an example of how to swap one child view controller for another using a transition animation. In this example, the new view controller is animated to the rectangle currently occupied by the existing child view controller, which is moved offscreen. After the animations finish, the completion block removes the child view controller from the container. In this example, the transitionFromViewController:toViewController:duration:options:animations:completion: method automatically updates the container’s view hierarchy, so you do not need to add and remove the views yourself.

清单5-3显示了如何使用过渡动画将一个子视图控制器交换为另一个子视图控制器的示例。 在此示例中,新视图控制器被动画化为当前由现有子视图控制器占用的矩形,该控制器在屏幕外移动。 动画完成后,完成块将从容器中删除子视图控制器。 在此示例中,transitionFromViewController:toViewController:duration:options:animations:completion:方法自动更新容器的视图层次结构,因此您无需自己添加和删除视图。

Listing 5-3Transitioning between two child view controllers

清单 5-3 两个子视图控制器之间的转换

- (void)cycleFromViewController: (UIViewController*) oldVC
               toViewController: (UIViewController*) newVC {
   // Prepare the two view controllers for the change.
   [oldVC willMoveToParentViewController:nil];
   [self addChildViewController:newVC];
 
   // Get the start frame of the new view controller and the end frame
   // for the old view controller. Both rectangles are offscreen.
   newVC.view.frame = [self newViewStartFrame];
   CGRect endFrame = [self oldViewEndFrame];
 
   // Queue up the transition animation.
   [self transitionFromViewController: oldVC toViewController: newVC
        duration: 0.25 options:0
        animations:^{
            // Animate the views to their final positions.
            newVC.view.frame = oldVC.view.frame;
            oldVC.view.frame = endFrame;
        }
        completion:^(BOOL finished) {
           // Remove the old view controller and send the final
           // notification to the new view controller.
           [oldVC removeFromParentViewController];
           [newVC didMoveToParentViewController:self];
        }];
}

Managing Appearance Updates for Children 管理子控制器的外观更新

After adding a child to a container, the container automatically forwards appearance-related messages to the child. 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 take over responsibility for appearance callbacks, override the shouldAutomaticallyForwardAppearanceMethods method in your container view controller and return NO, as shown in Listing 5-4. Returning NO lets UIKit know that your container view controller notifies its children of changes in its appearance.

要接管外观回调的责任,请覆盖容器视图控制器中的shouldAutomaticallyForwardAppearanceMethods方法并返回NO,如清单5-4所示。 返回NO让UIKit知道您的容器视图控制器通知其子项其外观的变化。

Listing 5-4 Disabling automatic appearance forwarding

清单 5-4 禁用自动外观转发

- (BOOL) shouldAutomaticallyForwardAppearanceMethods {
    return NO;
}

When an appearance transition occurs, call the child’s beginAppearanceTransition:animated: or endAppearanceTransition method as appropriate. 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 5-5.

发生外观转换时,请根据需要调用子项的beginAppearanceTransition:animated:endAppearanceTransition方法。 例如,如果您的容器具有子属性引用的单个子容器,则容器会将这些消息转发给子容器,如清单5-5所示。

Listing 5-5 Forwarding appearance messages when the container appears or disappears

清单 5-5 容器出现或消失时转发外观消息
在这里插入图片描述

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 the following tips when implementing your own container classes:

  • Access only the root view of a child view controller. A container should access only the root view of each child—that is, the view returned by the child’s view property. It should never access any of the child’s other views.
  • Child view controllers should have minimal knowledge of their container. A child view controller should focus on its own content. If the container allows its behavior to be influenced by a child, it should use the delegation design pattern to manage those interactions.
  • Design your container using regular views first. Using regular views (instead of the views from child view controllers) gives you an opportunity to test layout constraints and animated transitions in a simplified environment. When the regular views work as expected, swap them out for the views of your child view controllers.

设计,开发和测试新的容器视图控制器需要时间。 虽然个人行为很简单,但整个控制器可能非常复杂。 在实现自己的容器类时,请考虑以下提示:

  1. 仅访问子Vc的根视图。 容器应该只访问每个子节点的根视图 - 即子Vc的view属性返回的视图。 它永远不应该访问子Vc的任何其他视图。
  2. 子视图控制器应该对其容器知之甚少。 子视图控制器应该关注自己的内容。 如果容器允许其行为受到子项的影响,则应使用委派设计模式来管理这些交互。
  3. 首先使用常规视图设计容器。 使用常规视图(而不是子视图控制器中的视图)使您有机会在简化的环境中测试布局约束和动画过渡。 当常规视图按预期工作时,将它们换出以查看子视图控制器的视图。

Delegating Control to a Child View Controller(将控制委派给子视图控制器)

A container view controller can delegate some aspects of its own appearance to one or more of its children. You can delegate control in the following ways:

  • Let a child view controller determine the status bar style. To delegate the status bar appearance to a child, override one or both of the childViewControllerForStatusBarStyle and childViewControllerForStatusBarHidden methods in your container view controller.
  • Let the child specify its own preferred size. A container with a flexible layout can use the child’s own preferredContentSize property to help determine the size of the child.

个人注解, 仅供参考


  1. Implementing a Container View Controller; ↩︎

  2. 回到上级使用导航栏上的返回按钮,进入下级则靠子控制器中的按钮或TabView(点击push到下一页); ↩︎

  3. view controller 的简写; ↩︎

猜你喜欢

转载自blog.csdn.net/qfeung/article/details/88674997