容器视图控制器的创建(WORKING WITH CUSTOM CONTAINER VIEW CONTROLLERS)

本文介绍如何使用自定义容器视图控制器管理多个子控制器,包括DetailViewController的添加与删除过程,以及添加动画效果的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:http://www.thinkandbuild.it/working-with-custom-container-view-controllers/

我们将创建一个点击按钮弹出controller(点击按钮和弹出的次数没有限制)的应用。

下面简要说明如何实现


Container: 它继承自UIViewController,有一个subview属性,subview属性是其他子控制器和点击按钮后生成的新Detail Controller 共同的容器。

Detail: 它是一个简单的UIViewController,它包括一个标签和一个UIGestureRecognizer。在它的视图上滑动,标签的颜色会随机变化。此类的实例将成为容器视图控制器的子控制器。它的视图会赋值给容器的detailView,但是仍有自身管理,并接受手势事件。

源码:https://github.com/ariok/TB_ControllerContainment


添加子控制器

在正式开始之前,感谢FlatUI Kit (https://github.com/Grouper/FlatUIKit) 的作者Jack Flintermann.。FlatUI Kit 真是个特别炫酷的类库。

打开 ContainerViewController.m 找到presentDetailController 函数。这个函数完成新Detail controller加入容器视图控制器的操作。下面我们来看仔细代码


- (void)presentDetailController:(UIViewController*)detailVC{
     
    //0. Remove the current Detail View Controller showed 
    if(self.currentDetailViewController){
        [self removeCurrentDetailViewController];
    }
     
    //1. Add the detail controller as child of the container
    [self addChildViewController:detailVC];
     
    //2. Define the detail controller's view size
    detailVC.view.frame = [self frameForDetailController];
     
    //3. Add the Detail controller's view to the Container's detail view and save a reference to the detail View Controller
    [self.detailView addSubview:detailVC.view];
    self.currentDetailViewController = detailVC;
     
    //4. Complete the add flow calling the function didMoveToParentViewController
    [detailVC didMoveToParentViewController:self];
 
}


Step 0

此步骤我们一会儿再讨论,但这步的本质就是将当前正在显示的Detail ViewController 从容器中删除。

Step 1

IOS为了自定义容器控制器,加入了很多新的函数。addChildViewController 就是其中一个。就这么简单,我们在容器控制器中加了一个Detail Controller。

Step 2

新创建的Detail Controller的视图将要与容器控制器中预定义好的视图进行连接,所以我们要修改预定义视图的frame。

Step 3

现在,我们将Detail Controller的视图添加到容器的detailView上,并将新建的Detail Controller作为当前的Detail Controller。

Step 4

didMoveToParentViewController也是新加入UIViewController类的一个函数。调用这个函数后,我们发送给Detail Controller 实例一个消息,告诉它,它现在是其他控制器的子控制器。

删除子控制器

现在,我们来看 removeCurrentDetailViewController (与上面函数在同一.m文件中)

 Step 1

我们通过在willMoveToParentViewController 以nil 为参数,给current Detail Controller 发送一条信息,通知它将要从父控制器中移除。

Step 2

current Detail controller的视图从父视图中移除。

Step 3

通过调用标准函数removeFromParentViewController,我们将current Detail Controlle从容器中移除。当这个函数被调用的时候,Detail Controller会自动调用 didMoveToParentViewController,调用时的参数为nil。


结果

第一个Detail Controller在容器的viewDidLoad函数中被创建。initWithString:withColor函数帮我们创建一个DetailViewController实例。我们调用presentDetailController 函数,将刚刚创建的实例作为当前的Detail Controller (current Detail Controller)。

就像之前提到的,按钮在容器控制器的主视图上,当我们按下后,就展示一个新的Detail Controller。我们通过addDetailController来添加按钮事件,代码不多,如下:

- (IBAction)addDetailController:(id)sender {
    DetailViewController *detailVC = [[DetailViewController alloc]initWithString:@"This is a new viewController!" withColor:[UIColor asbestosColor]];
     
    /* Mode 1 */
    [self presentDetailController:detailVC];
     
    /* Mode 2 */
    //[self swapCurrentControllerWith:detailVC];
}

添加动画效果

还没有提到动画,下面说说。当我们push一个视图控制器的时候可以加入不同的动画,是个很好的想法。

我们不去分别创建方法来添加或删除Detail Controllers,而是将所有操作都放在一个名为swapCurrentControllerWith的函数,我们将新建的ViewController做为参数传入,下面是完整代码:

- (void)swapCurrentControllerWith:(UIViewController*)viewController{
     
    //1. The current controller is going to be removed
    [self.currentDetailViewController willMoveToParentViewController:nil];
     
    //2. The new controller is a new child of the container
    [self addChildViewController:viewController];
     
    //3. Setup the new controller's frame depending on the animation you want to obtain
    viewController.view.frame = CGRectMake(0, 2000, viewController.view.frame.size.width, viewController.view.frame.size.height);
 
    //The transition automatically removes the old view from the superview and attaches the new controller's view as child of the
    //container controller's view
     
    //Save the button position... we'll need it later
    CGPoint buttonCenter = self.button.center;
     
    [self transitionFromViewController:self.currentDetailViewController toViewController:viewController
                              duration:1.3 options:0
                            animations:^{
                                 
                                //The new controller's view is going to take the position of the current controller's view
                                viewController.view.frame = self.currentDetailViewController.view.frame;
                                 
                                //The current controller's view will be moved outside the window
                                self.currentDetailViewController.view.frame = CGRectMake(0,
                                                                                         -2000,
                                                                                         self.currentDetailViewController.view.frame.size.width,
                                                                                         self.currentDetailViewController.view.frame.size.width);
                                 
                                self.button.center = CGPointMake(buttonCenter.x,1000);
                                 
 
                            } completion:^(BOOL finished) {
                                //Remove the old view controller
                                [self.currentDetailViewController removeFromParentViewController];
                                 
                                //Set the new view controller as current
                                self.currentDetailViewController = viewController;
                                [self.currentDetailViewController didMoveToParentViewController:self];
                                 
                                //reset the button position
                                [UIView animateWithDuration:0.5 animations:^{
                                    self.button.center = buttonCenter;
                                }];
                                 
                            }];
}  

结论

如果你想添加一个新的子控制器并且删除之前的,按照以下步骤来做,这是每种导航模式里(navigation patterns  比如Navigation 、TabBar)共同的。

1 Current detail controller: [current willMoveToParentViewController:nil]     //当前的控制器
2 Next detail controller: [container addChildViewController:next]  //下一个控制器
2.B Next detail controller: [next willMoveToParentViewController:self] (自动调用2) 
3 Next detail controller: [container.view addSubview:next.view]
4 Current detail controller: [current.view removeFromSuperView]
5 Current detail controller: [current removeFromParentViewController]
6 Current detail controller: [current didMoveToParentViewController:nil] (自动被5调用)
7 Next detail controller: [next didMoveToParentViewController:self]

你可以利用这些新加入的函数来创建自己的导航模式。显然,UINavigationController 和 UITabBarController  就是很好的例子。

他们有不同的逻辑和不同的用处,但是他们有共同的地方,那就是每次只为用户展示一个View Controller。

另外一个关于容器控制器的例子是UISplitViewController,它可以一次展示给用户两个控制器。

简单说,你的自定义容器的本质任务就是管理controller,规定哪个需要显示,哪个不需要,如何让用户在controller之间进行切换。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值