Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/112.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 在容器视图中交换子视图_Ios_Objective C_Animation_Transitions_Uicontainerview - Fatal编程技术网

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

Ios 在容器视图中交换子视图,ios,objective-c,animation,transitions,uicontainerview,Ios,Objective C,Animation,Transitions,Uicontainerview,让ContainerView成为包含两个子内容视图的父容器视图:NavigationView和ContentView 我希望能够用另一个视图交换ContentView的控制器。例如,将主页控制器与新闻页控制器交换。目前,我能想到的唯一方法是使用委托告诉ContainerView我要切换视图。这似乎是一种草率的方法,因为ContainerViewController最终会为所有子视图指定一组特殊委托 这还需要与NavigationView通信,该视图包含有关当前在ContentView中的视图的

ContainerView
成为包含两个子内容视图的父容器视图:
NavigationView
ContentView

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

这还需要与
NavigationView
通信,该视图包含有关当前在
ContentView
中的视图的信息。例如:如果用户在新闻页面上,导航视图中的导航栏将显示新闻按钮当前处于选中状态

问题A: 是否有一种方法可以在
ContentView
中交换控制器,而无需委托方法调用
ContainerView
本身?我想以编程的方式来做这件事(没有故事板)

问题B:
如何从
NavigationView
交换
ContentView
中的控制器而无需委托调用?我希望以编程方式(无情节提要)完成此操作。

您似乎感到困惑。contentView(假设
UIView
)不“包含”您要交换的控制器。
UIViewController
处理其UIView的。在我看来,您需要一个父子视图控制器设置

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


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

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

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已经提供的方法除外)


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

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

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

  • 为此定义一个协议:

    @protocol ContainerParent <NSObject>
    
    - (void)changeContentTo:(UIViewController *)controller;
    
    @end
    
  • 子控制器现在可以参照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];
    }
    
    -(iAction)didTouchUpInsideButton:(id)发送方
    {
    id parentViewController=(id)self.parentViewController;
    NSAssert([parentViewController respondsToSelector:@selector(changeContentTo:)],@“父级必须符合ContainerParent协议”);
    UIViewController*newChild=…//以任何方式实例化新的子控制器
    [parentViewController将内容更改为:newChild];
    }
    

  • 对于此类转换,您还可以使用
    UIView.animateWith…
    animations

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

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

    您看过UIPageViewController吗?“页面视图控制器允许用户在内容页面之间导航,其中每个页面由其自己的视图控制器对象“@orbv”管理。此代码仅在父控制器中,因此不涉及重复。也许我不理解你的问题。Xcode 5只允许你在添加容器视图时使用嵌入序列。如果使用自动布局,我也会避免直接设置帧。@Abizern同意。如果以iOS 6及以上版本为目标,并使用故事板,我会这么做。但是OP说他想在没有故事板的情况下以编程的方式完成它。@orbv然后您将为此定义一个协议,然后让孩子在父视图控制器中调用适当的方法。这样,协议定义的接口就非常干净,但在所有子控制器中都不会重复复杂的自定义容器调用。请看修改后的答案。@Rob在再次阅读问题后,我明白你的意思了。不过,只是把它扔出去而已
    - (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];
    }
    
    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()
        }
    }