ios 在容器视图中交换子视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19162874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:00:43  来源:igfitidea点击:

Swapping child views in a container view

iosobjective-canimationtransitionsuicontainerview

提问by Alex

Let ContainerViewbe the parent container view with two child content views: NavigationViewand ContentView.

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

Example of View Layout

视图布局示例

I would like to be able to swap out the controller of ContentViewwith another view. For example, swapping a home page controller with a news page controller. Currently, the only way I can think to do this is by using a delegate to tell the ContainerViewthat I want to switch views. This seems like a sloppy way to do this because the ContainerViewControllerwould end up having a bunch of special delegates for all of the subviews.

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

This also needs to communicate with the NavigationViewwhich has information about which view is currently in the ContentView. For example: if the user is on the news page, the navigation bar within the navigation view will show that the news button is currently selected.

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

Question A:Is there a way to swap the controller in ContentViewwithout a delegate method calling the ContainerViewitself? I would like to do this programmatically (no storyboard).

问题 A:有没有办法在ContentView没有委托方法调用ContainerView自身的情况下交换控制器?我想以编程方式执行此操作(没有故事板)。

Question B:How can I swap controllers in ContentViewfrom the NavigationViewwithout a delegate call? I would like to do this programmatically (no storyboard).

问题 B:如何ContentViewNavigationView没有委托调用的情况下交换控制器?我想以编程方式执行此操作(没有故事板)。

回答by Rob

When you have child views that have their own view controllers, you should be following the custom container controller pattern. See Creating Custom Container View Controllersfor more information.

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

Assuming you've followed the custom container pattern, when you want to change the child view controller (and its associated view) for the "content view", you would do that programmatically with something like:

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

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

When you do it this way, there's no need for any delegate-protocol interface with the content view's controller (other than what iOS already provides with the Managing Child View Controllers in a Custom Containermethods).

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



By the way, this assumes that the initial child controller associated with that content view was added like so:

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

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


If you want a child controller to tell the parent to change the controller associated with the content view, you would:

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

  1. Define a protocol for this:

    @protocol ContainerParent <NSObject>
    
    - (void)changeContentTo:(UIViewController *)controller;
    
    @end
    
  2. Define the parent controller to conform to this protocol, e.g.:

    #import <UIKit/UIKit.h>
    #import "ContainerParent.h"
    
    @interface ViewController : UIViewController <ContainerParent>
    
    @end
    
  3. Implement the changeContentTomethod in the parent controller (much as outlined above):

    - (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. And the child controllers can now use this protocol in reference to the self.parentViewControllerproperty that iOS provides for you:

    - (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];
    }
    
  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. 并且子控制器现在可以参考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];
    }
    

回答by gbk

For such kind of transition you can also use UIView.animateWith...animations.

对于这种过渡,您还可以使用UIView.animateWith...动画。

For example, assume that rootContainerViewis container and contentViewControllercurrently active controller in container, then

例如,假设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()
    }
}

this will provide u simple fade animations, u can also can try any UIViewAnimationOptions, transitions etc.

这将为您提供简单的淡入淡出动画,您也可以尝试任何UIViewAnimationOptions,过渡等。

回答by Tim

You seem to be getting confused. A contentView (assuming UIView) does not 'contain' a controller that you would swap out. A UIViewControllerhandles its UIView's. It seems to me that you require a parent-child view controller setup.

你好像有点糊涂了。contentView(假设UIView)不“包含”您要换出的控制器。AUIViewController处理它的 UIView。在我看来,您需要设置父子视图控制器。

A single parent view controller, that would handle child view controllers, each of which you can then handle when each is shown on the screen and adjust your UIViews and content accordingly. Please see the Apple documentation below.

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

Container Programming - Apple Documentation

容器编程 - Apple 文档