在容器视图中交换子视图

Ale*_*lex 22 animation objective-c transitions ios uicontainerview

让我们ContainerView与两个子内容视图父容器视图:NavigationViewContentView.

视图布局示例

我希望能够将控制器换成ContentView另一个视图.例如,使用新闻页面控制器交换主页控制器.目前,我能想到的唯一方法是使用委托来告诉ContainerView我要切换视图.这似乎是一种草率的方式,因为最终ContainerViewController会为所有子视图提供一堆特殊的委托.

这还需要与NavigationView具有关于当前哪个视图的信息的人进行通信ContentView.例如:如果用户在新闻页面上,导航视图中的导航栏将显示当前选择了新闻按钮.

问题A: 有没有办法在ContentView没有调用ContainerView自身的委托方法的情况下交换控制器?我想以编程方式(没有故事板)这样做.

问题B: 如何ContentViewNavigationView没有委托电话的情况下交换控制器?我想以编程方式(没有故事板)这样做.

Rob*_*Rob 38

当您拥有具有自己的视图控制器的子视图时,您应该遵循自定义容器控制器模式.有关更多信息,请参阅创建自定义容器视图控制器

假设您已经遵循自定义容器模式,当您想要更改"内容视图"的子视图控制器(及其关联视图)时,您可以通过以下方式以编程方式执行此操作:

UIViewController *newController = ... // instantiate new controller however you want
UIViewController *oldController = ... // grab the existing controller for the current "content view"; perhaps you maintain this in your own ivar; perhaps you just look this up in self.childViewControllers

newController.view.frame = oldController.view.frame;

[oldController willMoveToParentViewController:nil];
[self addChildViewController:newController];         // incidentally, this does the `willMoveToParentViewController` for the new controller for you

[self transitionFromViewController:oldController
                  toViewController:newController
                          duration:0.5
                           options:UIViewAnimationOptionTransitionCrossDissolve
                        animations:^{
                            // no further animations required
                        }
                        completion:^(BOOL finished) {
                            [oldController removeFromParentViewController]; // incidentally, this does the `didMoveToParentViewController` for the old controller for you
                            [newController didMoveToParentViewController:self];
                        }];
Run Code Online (Sandbox Code Playgroud)

当您这样做时,不需要与内容视图的控制器的任何委托协议接口(除了iOS已经在自定义容器方法中管理子视图控制器提供的接口).


顺便说一下,这假设与该内容视图关联的初始子控制器是这样添加的:

UIViewController *childController = ... // instantiate the content view's controller any way you want
[self addChildViewController:childController];
childController.view.frame = ... // set the frame any way you want
[self.view addSubview:childController.view];
[childController didMoveToParentViewController:self];
Run Code Online (Sandbox Code Playgroud)

如果您希望子控制器告诉父级更改与内容视图关联的控制器,您将:

  1. 为此定义协议:

    @protocol ContainerParent <NSObject>
    
    - (void)changeContentTo:(UIViewController *)controller;
    
    @end
    
    Run Code Online (Sandbox Code Playgroud)
  2. 定义父控制器以符合此协议,例如:

    #import <UIKit/UIKit.h>
    #import "ContainerParent.h"
    
    @interface ViewController : UIViewController <ContainerParent>
    
    @end
    
    Run Code Online (Sandbox Code Playgroud)
  3. changeContentTo在父控制器中实现该方法(如上所述):

    - (void)changeContentTo:(UIViewController *)controller
    {
        UIViewController *newController = controller;
        UIViewController *oldController = ... // grab reference of current child from `self.childViewControllers or from some property where you stored it
    
        newController.view.frame = oldController.view.frame;
    
        [oldController willMoveToParentViewController:nil];
        [self addChildViewController:newController];
    
        [self transitionFromViewController:oldController
                          toViewController:newController
                                  duration:1.0
                                   options:UIViewAnimationOptionTransitionCrossDissolve
                                animations:^{
                                    // no further animations required
                                }
                                completion:^(BOOL finished) {
                                    [oldController removeFromParentViewController];
                                    [newController didMoveToParentViewController:self];
                                }];
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 现在,子控制器可以使用此协议来引用self.parentViewControlleriOS为您提供的属性:

    - (IBAction)didTouchUpInsideButton:(id)sender
    {
        id <ContainerParent> parentViewController = (id)self.parentViewController;
        NSAssert([parentViewController respondsToSelector:@selector(changeContentTo:)], @"Parent must conform to ContainerParent protocol");
    
        UIViewController *newChild = ... // instantiate the new child controller any way you want
        [parentViewController changeContentTo:newChild];
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • @Rob在再次阅读问题之后,我明白了你的意思.但是把它扔到那里.;) (2认同)