iPhone – closures多个ViewControllers
我有一个很长的视图控制器层次结构;
在第一个视图控制器我使用这个代码:
SecondViewController *svc = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil]; [self presentModalViewController:svc animated:YES]; [svc release];
在第二个视图控制器我使用这个代码:
ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil]; [self presentModalViewController:tvc animated:YES]; [tvc release];
等等。
所以有一个时刻,我有很多视图控制器,我需要回到第一个视图控制器。 如果我一次回来一步,我在每个视图控制器中使用这个代码:
[self dismissModalViewControllerAnimated:YES];
如果我想从第六个视图控制器直接返回到第一个视图控制器,我必须做什么来一次解散所有的控制器?
谢谢
是。 已经有了一堆答案,但是我只是要添加一个到列表的最后。 问题是我们需要在层次结构的基础上获取对视图控制器的引用。 就像@Juan Munhoes Junior的回答一样,你可以走等级制,但是用户可能有不同的路线,所以这是一个非常脆弱的答案。 扩展这个简单的解决scheme并不难,尽pipe只是简单地遍历层次结构来寻找堆栈的底部。 在底部的呼叫解散也将得到所有其他人。
-(void)dismissModalStack { UIViewController *vc = self.presentingViewController; while (vc.presentingViewController) { vc = vc.presentingViewController; } [vc dismissViewControllerAnimated:YES completion:NULL]; }
这是简单而灵活的:如果你想在堆栈中寻找一种特定types的视图控制器,你可以添加基于[vc isKindOfClass:[DesiredViewControllerClass class]]
逻辑。
我find了解决scheme。
当然,你可以在最明显的地方find解决scheme,以便从UIViewController参考中为dismissModalViewControllerAnimated方法读取…
如果您连续呈现几个模式视图控制器,从而构build一堆模式视图控制器,则在堆栈中较低视图控制器上调用此方法将取消其立即子视图控制器以及堆栈上该子视图之上的所有视图控制器。 当发生这种情况时,只有最顶层的观点才会以animation的方式被驳回。 任何中间视图控制器都可以简单地从堆栈中移除。 最顶层的视图使用其模式转换样式,这可能不同于其他视图控制器在栈中使用的样式。
所以在目标视图上调用dismissModalViewControllerAnimated就足够了。 我使用了下面的代码:
[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];
回到我家。
假设您的第一个视图控制器也是根/初始视图控制器(您在故事板中被提名为初始视图控制器的那个控制器)。 您可以将其设置为侦听请求以closures其提供的所有视图控制器:
在FirstViewController中:
- (void)viewDidLoad { [super viewDidLoad]; // listen to any requests to dismiss all stacked view controllers [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissAllViewControllers:) name:@"YourDismissAllViewControllersIdentifier" object:nil]; // the remainder of viewDidLoad ... } // this method gets called whenever a notification is posted to dismiss all view controllers - (void)dismissAllViewControllers:(NSNotification *)notification { // dismiss all view controllers in the navigation stack [self dismissViewControllerAnimated:YES completion:^{}]; }
在任何其他的视图控制器下导航堆栈,决定我们应该返回到导航堆栈的顶部:
[[NSNotificationCenter defaultCenter] postNotificationName:@"YourDismissAllViewControllersIdentifier" object:self];
这应该消除所有模态提供的视图控制器的animation,只留下根视图控制器。 如果你的初始视图控制器是一个UINavigationController并且第一个视图控制器被设置为它的根视图控制器,这也可以工作。
奖金提示:通知名称相同是非常重要的。 在应用程序的某个地方定义这个通知名称可能是一个好主意,因为不会由于input错误而导致沟通不畅。
iOS 8+的全屏解雇的通用方法没有错误的animation上下文。 在Objective-C和Swift中
Objective-C的
- (void)dismissModalStackAnimated:(bool)animated completion:(void (^)(void))completion { UIView *fullscreenSnapshot = [[UIApplication sharedApplication].delegate.window snapshotViewAfterScreenUpdates:false]; [self.presentedViewController.view insertSubview:fullscreenSnapshot atIndex:NSIntegerMax]; [self dismissViewControllerAnimated:animated completion:completion]; }
迅速
func dismissModalStack(animated: Bool, completion: (() -> Void)?) { if let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false) { presentedViewController?.view.addSubview(fullscreenSnapshot) } if !isBeingDismissed { dismiss(animated: animated, completion: completion) } }
TL;博士
其他解决scheme有什么问题?
有很多解决scheme,但是他们都没有计算错误的解雇背景:
例如, 根A – >呈现B – >呈现C,并且您想从C中退出到A,您可以通过在dismissViewControllerAnimated
上调用dismissViewControllerAnimated
来rootViewController
。
[[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];
但是,从C中调用这个根,会导致正确的错误转换行为(B代替A代替C代替A)。
所以
我创build了通用解雇方法。 此方法将采用当前的全屏快照,并将其放在接收器所呈现的视图控制器上,然后将其全部closures。 (例如:被称为C的默认解雇,但B真的被视为解雇)
[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];
你也可以在你想要closures的所有控制器中实现一个委托
如果您使用的都是模型视图控制器,则可以使用通知来closures所有预先设置的视图控制器。
1.像这样在RootViewController中注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissModelViewController) name:dismissModelViewController object:nil];
2.在rootviewController中实现dismissModelViewController函数
- (void)dismissModelViewController { While (![self.navigationController.visibleViewController isMemberOfClass:[RootviewController class]]) { [self.navigationController.visibleViewController dismissViewControllerAnimated:NO completion:nil]; } }
3.通知postclosures或解除button事件。
[[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];
在Swift中:
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
尝试这个..
ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil]; [self.view addsubview:tvc]; [tvc release];
首先谢谢你的代码。
要开始你的navigationController,你可以使它更加dynamic一点。 (如果你不知道堆栈中ViewController的数量)
NSArray *viewControllers = self.navigationController.viewControllers; [self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
id vc = [self presentingViewController]; id lastVC = self; while (vc != nil) { id tmp = vc; vc = [vc presentingViewController]; lastVC = tmp; } [lastVC dismissViewControllerAnimated:YES completion:^{ }];
使用这个通用的解决scheme..来解决这个问题..
- (UIViewController*)topViewController { UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController; while (topController.presentedViewController) { topController = topController.presentedViewController; } return topController; } - (void)dismissAllModalController{ __block UIViewController *topController = [self topViewController]; while (topController.presentingViewController) { [topController dismissViewControllerAnimated:NO completion:^{ }]; topController = [self topViewController]; } }
请使用这个..快乐编码:)
这里是我用来popup和解除所有视图控制器以便返回到根视图控制器的解决scheme。 我在UIViewController类中有这两个方法:
+ (UIViewController*)topmostViewController { UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController]; while(vc.presentedViewController) { vc = vc.presentedViewController; } return vc; } + (void)returnToRootViewController { UIViewController* vc = [UIViewController topmostViewController]; while (vc) { if([vc isKindOfClass:[UINavigationController class]]) { [(UINavigationController*)vc popToRootViewControllerAnimated:NO]; } if(vc.presentingViewController) { [vc dismissViewControllerAnimated:NO completion:^{}]; } vc = vc.presentingViewController; } }
那我就打电话
[UIViewController returnToRootViewController];
一个迅速的版本有一些补充基于这个评论
func dismissModalStack(viewController: UIViewController, animated: Bool, completionBlock: BasicBlock?) { if viewController.presentingViewController != nil { var vc = viewController.presentingViewController! while (vc.presentingViewController != nil) { vc = vc.presentingViewController!; } vc.dismissViewControllerAnimated(animated, completion: nil) if let c = completionBlock { c() } } }
Swift 3扩展基于上述答案。
这样一个堆栈的原则:A – > B – > C – > D
- 拍一张D的快照
- 在B上添加此快照
- 从没有animation的B中解散
-
完成后,用animation解除A
extension UIViewController { func dismissModalStack(animated: Bool, completion: (() -> Void)?) { let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false) if !isBeingDismissed { var rootVc = presentingViewController while rootVc?.presentingViewController != nil { rootVc = rootVc?.presentingViewController } let secondToLastVc = rootVc?.presentedViewController if fullscreenSnapshot != nil { secondToLastVc?.view.addSubview(fullscreenSnapshot!) } secondToLastVc?.dismiss(animated: false, completion: { rootVc?.dismiss(animated: true, completion: completion) }) } } }
在模拟器上有点闪烁,但不在设备上。
基于上述答案的Swift扩展:
extension UIViewController { func dismissUntilAnimated<T: UIViewController>(animated: Bool, viewController: T.Type, completion: ((viewController: T) -> Void)?) { var vc = presentingViewController! while let new = vc.presentingViewController where !(new is T) { vc = new } vc.dismissViewControllerAnimated(animated, completion: { completion?(viewController: vc as! T) }) } }
Swift 3.0版本:
extension UIViewController { /// Dismiss all modally presented view controllers until a specified view controller is reached. If no view controller is found, this function will do nothing. /// - Parameter reached: The type of the view controller to dismiss until. /// - Parameter flag: Pass `true` to animate the transition. /// - Parameter completion: The block to execute after the view controller is dismissed. This block contains the instance of the `presentingViewController`. You may specify `nil` for this parameter. func dismiss<T: UIViewController>(until reached: T.Type, animated flag: Bool, completion: ((T) -> Void)? = nil) { guard let presenting = presentingViewController as? T else { return presentingViewController?.dismiss(until: reached, animated: flag, completion: completion) ?? () } presenting.dismiss(animated: flag) { completion?(presenting) } } }
完全忘了为什么我这样做,因为它是令人难以置信的愚蠢的逻辑考虑大部分时间一个模态视图控制器呈现视图控制器是UITabBarController
呈现这完全无用的。 实际上获取基本视图控制器实例和调用dismiss
是非常有意义的。
closures顶级的VCanimation,其他的不是。 如果你有三个模态风险投资
[self dismissModalViewControllerAnimated:NO]; // First [self dismissModalViewControllerAnimated:NO]; // Second [self dismissModalViewControllerAnimated:YES]; // Third
编辑:如果你想这样做只有一种方法,将您的层次结构保存到一个VC数组,并closures最后一个对象的animation和其他的不是。
简单的recursion更接近:
extension UIViewController { final public func dismissEntireStackAndSelf(animate: Bool = true) { // Always false on non-calling controller presentedViewController?.ip_dismissEntireStackAndSelf(false) self.dismissViewControllerAnimated(animate, completion: nil) } }
这将迫使closures每个孩子的控制器,然后只有自己的animation。 你可以切换任何你喜欢的,但如果你animation每个控制器,他们去一个一个,它很慢。
呼叫
baseController.dismissEntireStackAndSelf()
关于解雇的苹果文件(animation:完成:)方法。
在Discussion
部分,它说:
any intermediate view controllers are simply removed from the stack.
如果您连续呈现多个视图控制器,从而构build呈现的视图控制器堆栈,则在堆栈中较低视图控制器上调用此方法将取消其立即子视图控制器以及该堆栈上的该子视图之上的所有视图控制器。 当发生这种情况时,只有最顶层的观点才会以animation的方式被驳回。 任何中间视图控制器都可以简单地从堆栈中移除。 最顶层的视图使用其模式转换样式,这可能不同于其他视图控制器在栈中使用的样式。
换句话说,如果视图控制器堆栈如下
Root -> A -> B -> C -> D ... -> Z
D
调用dismiss
方法时,所有视图控制器的隐藏D
,例如: (E ... Z)
将被从堆栈中删除。
对于Swift 3.0+
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
这将消除你的rootviewcontroller上所有呈现的视图控制器。
如果你直接回到开始,你可以使用代码[self.navigationController popToRootViewControllerAnimated:YES];