iPhone-解散多个ViewController
-
05-10-2019 - |
题
我有一个长的视图控制器层次结构;
在第一个视图控制器中,我使用此代码:
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];
如果我想直接从第六视图控制器返回到第一个视图控制器,我该怎么做才能立即解雇所有控制器?
谢谢
解决方案 2
我找到了解决方案。
当然,您可以在最明显的位置找到解决方案,因此从UiviewController参考中读取divionModalViewControllerAnimated方法...
如果您连续介绍几个模态视图控制器,从而构建了一堆模态视图控制器,则在堆栈中较低的视图控制器上称此方法将其直接的儿童视图控制器和所有视图控制器拒绝堆栈上的孩子。发生这种情况时,只有最高的视野以动画方式被忽略。任何中间视图控制器都会简单地从堆栈中删除。最高视图是使用其模态过渡样式忽略的,这可能与堆栈中其他视图控制器使用的样式不同。
因此,在目标视图上拨打解雇的模块化controlleranimated是足够的。我使用以下代码:
[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];
回到我家。
其他提示
是的。已经有很多答案,但是无论如何我都会在列表的末尾添加一个答案。问题在于,我们需要在层次结构底部获取对视图控制器的引用。就像在@Juan Munhoes Junior的答案中一样,您可以走层次结构,但是用户可能会采取不同的路线,因此这是一个非常脆弱的答案。不难扩展这个简单的解决方案,尽管简单地将层次结构漫步在寻找堆栈的底部。在底部召集解雇也将获得其他所有人。
-(void)dismissModalStack {
UIViewController *vc = self.presentingViewController;
while (vc.presentingViewController) {
vc = vc.presentingViewController;
}
[vc dismissViewControllerAnimated:YES completion:NULL];
}
这很简单且灵活:如果您想在堆栈中查找特定类型的视图控制器,则可以基于 [vc isKindOfClass:[DesiredViewControllerClass class]]
.
iOS 8+通用方法用于全屏解雇,而无需错误的动画上下文。 在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; dr
其他解决方案有什么问题?
有很多解决方案,但它们都没有算是错误地驳回上下文,因此:
例如 根A->礼物B->礼物c 您想从C中解雇A,您可以通过致电 dismissViewControllerAnimated
上 rootViewController
.
[[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];
然而 从C上解雇的呼叫解雇将导致正确的行为以错误的过渡(B到A),而不是C到A)。
所以
我创建了普遍解雇方法。这个方法 将采用当前的全屏快照,并将其放置在接收器呈现的视图控制器上,然后将其全部删除。 (示例:称为默认解雇C,但B被视为被视为解雇)
假设您的第一个视图控制器也是根 /初始视图控制器(您在故事板中提名为初始视图控制器的控制器)。您可以将其设置为聆听请求以驳回其所有提出的视图控制器:
在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];
这应该可以用动画否定所有模态呈现的视图控制器,仅留下根视图控制器。如果您的初始视图控制器是UInavigationController,并且将第一个视图控制器设置为其root View Controller,则此功能也可以。
奖励提示:重要的是通知名称相同。将此通知名称定义为应用程序中的一个变量可能是一个好主意,因为键入错误而不会因误差而沟通。
[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];
您还可以在要解雇的所有控制器中实施代表
如果您使用的所有是模型视图控制器,则可以使用通知来删除所有预设视图控制器。
1.在rootviewController中注册通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dismissModelViewController)
name:dismissModelViewController
object:nil];
2. rootviewController中的dimplomodelViewController函数进行implement
- (void)dismissModelViewController
{
While (![self.navigationController.visibleViewController isMemberOfClass:[RootviewController class]])
{
[self.navigationController.visibleViewController dismissViewControllerAnimated:NO completion:nil];
}
}
3.注释发布每个关闭或解雇按钮事件。
[[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];
在Swift:
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
大多数解决方案的问题是,当您删除呈现的视图控制器的堆栈时,用户将在被丢弃时简要看到堆栈中第一个呈现的ViewController。 Jakub的出色解决方案解决了这一点。这是基于他的回答的扩展名。
extension UIViewController {
func dismissAll(animated: Bool, completion: (() -> Void)? = nil) {
if let optionalWindow = UIApplication.shared.delegate?.window, let window = optionalWindow, let rootViewController = window.rootViewController, let presentedViewController = rootViewController.presentedViewController {
if let snapshotView = window.snapshotView(afterScreenUpdates: false) {
presentedViewController.view.addSubview(snapshotView)
presentedViewController.modalTransitionStyle = .coverVertical
}
if !isBeingDismissed {
rootViewController.dismiss(animated: animated, completion: completion)
}
}
}
}
用法:从您要解散回根的任何呈现的ViewController中调用此扩展功能。
@IBAction func close() {
dismissAll(animated: true)
}
尝试这个..
ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];
[tvc release];
首先,奥斯卡peli感谢您的代码。
为了在开始时启动您的导航控制器,您可以通过这种方式使其更具动态性。 (如果您不知道堆栈中的ViewControllers数量)
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:^{
}];
这是我用来弹出和删除所有视图控制器的解决方案,以返回根视图控制器。我在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];
Swift版本,基于一些添加 这个 评论
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()
}
}
}
简单的递归紧密:
extension UIViewController {
final public func dismissEntireStackAndSelf(animate: Bool = true) {
// Always false on non-calling controller
presentedViewController?.ip_dismissEntireStackAndSelf(false)
self.dismissViewControllerAnimated(animate, completion: nil)
}
}
这将迫使每个儿童控制器关闭,然后只会动画自我。您可以为您喜欢的任何东西切换,但是如果您为每个控制器进行动画操作,它们会一个一个逐渐变速,而且速度很慢。
称呼
baseController.dismissEntireStackAndSelf()
Swift 3 基于上述答案的扩展。
这样的堆栈原理:a-> b-> c-> d
- 拍摄D
- 在B上添加此快照
- 解雇无动画
完成后,从动画中解雇
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
在那。
对于Swift 3.0+
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
这将删除您的rootviewController上所有呈现的视图控制器。
使用此通用解决方案来解决此问题:
- (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];
}
}
解除动画的顶级风险投资,而不是其他动画。如果您有三个模态VC
[self dismissModalViewControllerAnimated:NO]; // First
[self dismissModalViewControllerAnimated:NO]; // Second
[self dismissModalViewControllerAnimated:YES]; // Third
编辑:如果您只想使用一种方法执行此操作,请将层次结构保存到一个VC数组中,并将最后一个对象动画,而其他对象则没有。
苹果文件 解雇(动画:完成:) 方法。
在节中 Discussion
, , 它说:
any intermediate view controllers are simply removed from the stack.
如果您连续介绍几个视图控制器,从而构建了一堆呈现的视图控制器,在堆栈中较低的视图控制器上称此方法将其直接的儿童视图控制器和所有视图控制器拒绝堆栈上的孩子上方。发生这种情况时,只有最高的视野以动画方式被忽略。 任何中间视图控制器都会简单地从堆栈中删除。 最高视图是使用其模态过渡样式忽略的,这可能与堆栈中其他视图控制器使用的样式不同。
换句话说,如果视图控制器堆叠如下
Root -> A -> B -> C -> D ... -> Z
D
呼叫 dismiss
方法,所有查看控制器behide D
, , 前任: (E ... Z)
, ,将从堆栈中删除。
在 Swift 4 和 XCode 9 这将对您有所帮助。
var vc : UIViewController = self.presentingViewController!
while ((vc.presentingViewController) != nil) {
vc = vc.presentingViewController!
}
vc.dismiss(animated: true, completion: nil)
享受 !!! :)
如果您要返回开始,则可以使用代码[self.navigationController popTorOtViewControllerRollerAnimated:yes];