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
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()
}
}