在容器视图中交换子视图

ContainerView成为具有两个子内容视图的父容器视图: NavigationViewContentView

视图布局示例

我希望能够用另一个视图replaceContentView的控制器。 例如,交换一个主页控制器与新闻页面控制器。 目前,唯一可以这样做的方法是使用委托来告诉ContainerView我要切换视图。 这似乎是一个马虎的方式来做到这一点,因为ContainerViewController将最终有一大堆的所有子视图的特殊代表。

这也需要与具有关于当前在ContentView中的哪个视图的信息的NavigationView进行通信。 例如:如果用户在新闻页面上,导航视图中的导航栏将显示新闻button当前被选中。

问题A:有没有办法在ContentView交换控制器,而无需调用ContainerView本身的委托方法? 我想以编程方式(没有故事板)做到这一点。

问题B:如何在没有委托调用的情况下从NavigationView切换ContentView控制器? 我想以编程方式(没有故事板)做到这一点。

当您有子视图有自己的视图控制器时,您应该按照自定义容器控制器模式。 请参阅创build自定义容器视图控制器了解更多信息。

假设你已经遵循了自定义容器模式,当你想要改变“内容视图”的子视图控制器(及其相关的视图)时,你可以用下面的代码来编程:

 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]; }]; 

当你这样做的时候,不需要任何与内容视图的控制器有关的委托协议接口(除了iOS已经提供的在自定义容器方法中pipe理子视图控制器的东西外 )。


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

 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]; 

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

  1. 为此定义一个协议:

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

     #import <UIKit/UIKit.h> #import "ContainerParent.h" @interface ViewController : UIViewController <ContainerParent> @end 
  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]; }]; } 
  4. 而且子控制器现在可以使用这个协议来引用iOS为你提供的self.parentViewController属性:

     - (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]; } 

对于这种types的转换,你也可以使用UIView.animateWith...animation。

例如,假设rootContainerView是容器,而contentViewController当前在容器中的活动控制器

 func setContentViewController(contentViewController:UIViewController, animated:Bool = true) { if animated == true { addChildViewController(contentViewController) contentViewController.view.alpha = 0 contentViewController.view.frame = rootContainerView.bounds rootContainerView.addSubview(contentViewController.view) self.contentViewController?.willMoveToParentViewController(nil) UIView.animateWithDuration(0.3, animations: { contentViewController.view.alpha = 1 }, completion: { (_) in contentViewController.didMoveToParentViewController(self) self.contentViewController?.view.removeFromSuperview() self.contentViewController?.didMoveToParentViewController(nil) self.contentViewController?.removeFromParentViewController() self.contentViewController = contentViewController }) } else { cleanUpChildControllerIfPossible() contentViewController.view.frame = rootContainerView.bounds addChildViewController(contentViewController) rootContainerView.addSubview(contentViewController.view) contentViewController.didMoveToParentViewController(self) self.contentViewController = contentViewController } } // MARK: - Private private func cleanUpChildControllerIfPossible() { if let childController = contentViewController { childController.willMoveToParentViewController(nil) childController.view.removeFromSuperview() childController.removeFromParentViewController() } } 

这将提供你简单的淡入淡出animation,你也可以尝试任何UIViewAnimationOptions ,转换等。

你似乎感到困惑。 一个contentView(假设UIView )不包含你将换出的控制器。 一个UIViewController处理它的UIView的。 在我看来,你需要一个父母子视图控制器设置。

一个父视图控制器,它将处理子视图控制器,每个控制器都可以在屏幕上显示每个控制器时处理,并相应地调整您的UIViews和内容。 请参阅下面的Apple文档。

容器编程 – 苹果文档