一次关闭多个模态视图控制器?

Posted

技术标签:

【中文标题】一次关闭多个模态视图控制器?【英文标题】:Dismissing multiple modal view controllers at once? 【发布时间】:2011-03-14 20:55:58 【问题描述】:

所以有一个包含三个视图控制器的堆栈,其中 A 是根,B 是第一个模态视图控制器,C 是第三个模态 vc。我想立即从 C 转到 A。我试过this solution to dismiss。它确实工作,但方式不正确。也就是说,当最后一个视图控制器被关闭时,它会在显示第一个视图控制器之前简要显示第二个视图控制器。我正在寻找的是一种在一个不错的动画中从第三个 vc 到第一个的方法,而不会注意到第二个视图。非常感谢您对此的任何帮助。

【问题讨论】:

解除多个模式视图控制器的更通用方法是here 【参考方案1】:

请确保您只调用一次dismissModalViewControllerAnimated:

我发现要求关闭每个堆叠的模态视图控制器会导致它们都产生动画。

你有:A =modal> B =modal> C

你应该只打电话给[myViewControllerA dismissModalViewControllerAnimated:YES]

如果您使用[myViewControllerB dismissModalViewControllerAnimated:YES],它将关闭 C,而不是 B。在正常(未堆叠)使用中,它将关闭 B(由于响应者链将消息冒泡到 A)。在您描述的堆叠场景中,B 是父视图控制器,这优先于作为模态视图控制器。

【讨论】:

现在我使用:[[[self parentViewController] parentViewController]dismissModalViewControllerAnimated:YES];那将使用根视图控制器。但是,它仍然显示第二个 vc 短时间。 哦,我应该说我使用的是 Utility 模板,第二个 vc 是反面视图。第三个是从反面实例化的模态vc。 注意:在 ios5 中,这已更改为“presentingViewController”game4mob.com/index.php/jawbreaker/…【参考方案2】:

虽然接受的答案确实对我有用,但它现在可能已经过时并且留下了一个看起来很奇怪的动画,其中最上面的模态会立即消失并且动画将在后模态视图上。我尝试了很多方法来避免这种情况,最终不得不使用一些技巧让它看起来不错。 注意:(只在iOS8+测试,iOS7+应该可以)

基本上,viewControllerA 会创建一个UINavigationController,以viewControllerB 作为根视图并以模态方式呈现它。

// ViewControllerA.m
- (void)presentViewB 
    ViewControllerB *viewControllerB = [[ViewControllerB alloc] init];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewControllerB];

    navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
    [self presentViewController:navigationController animated:YES completion:nil];

现在在viewControllerB 中,我们将以相同的方式呈现viewControllerC,但在呈现之后,我们将在viewControllerB 的导航控制器上的视图层上放置viewControllerC 的快照。然后,当viewControllerC在解雇期间消失时,我们将看不到变化,动画看起来很漂亮。

//ViewControllerB.m
- (void)presentViewC 
    ViewControllerC *viewControllerC = [[ViewControllerC alloc] init];

    // Custom presenter method to handle setting up dismiss and snapshotting 
    // I use this in a menu that can present many VC's so I centralized this part.
    [self presentViewControllerForModalDismissal:viewControllerC];

以下是用于呈现视图和处理解雇的辅助函数。 需要注意的一点是,我使用 Purelayout 来添加自动布局约束。您可以修改它以手动添加它们或在以下位置获取 Purelayout https://github.com/PureLayout/PureLayout

#pragma mark - Modal Presentation Helper functions
- (void)presentViewControllerForModalDismissal:(UIViewController*)viewControllerToPresent 
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewControllerToPresent];
    navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

    // Ensure that anything we are trying to present with this method has a dismissBlock since I don't want to force everything to inherit from some base class. 
    NSAssert([viewControllerToPresent respondsToSelector:NSSelectorFromString(@"dismissBlock")], @"ViewControllers presented through this function must have a dismissBlock property of type (void)(^)()");
    [viewControllerToPresent setValue:[self getDismissalBlock] forKey:@"dismissBlock"];

    [self presentViewController:navigationController animated:YES completion:^
        // We want the presented view and this modal menu to dismiss simultaneous. The animation looks weird and immediately becomes the menu again when dismissing.
        // So we are snapshotting the presented view and adding it as a subview so you won't see the menu again when dismissing.
        UIView *snapshot = [navigationController.view snapshotViewAfterScreenUpdates:NO];
        [self.navigationController.view addSubview:snapshot];
        [snapshot autoPinEdgesToSuperviewEdges];
    ];


- (void(^)()) getDismissalBlock 
    __weak __typeof(self) weakSelf = self;
    void(^dismissBlock)() = ^
        __typeof(self) blockSafeSelf = weakSelf;
        [blockSafeSelf.navigationController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    ;

    return dismissBlock;

现在我们只需要确保在 ViewControllerC.h 中定义了dismissBlock 属性(您显然可以将整个部分替换为委托方法或其他同样令人兴奋的设计模式,重要的部分是在 viewControllerB 处理解除等级)

// ViewControllerC.h
@interface ViewControllerC : UIViewController
@property (nonatomic, copy) void (^dismissBlock)(void);
@end

//ViewControllerC.m
// Make an method to handle dismissal that is called by button press or whatever logic makes sense.
- (void)closeButtonPressed 
    if (_dismissBlock)  // If the dismissblock property was set, let the block handle dismissing
        _dismissBlock();
        return;
    

    // Leaving this here simply allows the viewController to be presented modally as the base as well or allow the presenter to handle it with a block.
    [self dismissViewControllerAnimated:YES completion:nil];

希望这会有所帮助,祝你编程愉快:)

【讨论】:

赞成一个可行的解决方案,但这太疯狂了(与其好处相比) 是的,但实际上没有一个简单且看起来不错的工作解决方案。我很沮丧苹果还没有很好的方法来处理这个问题。希望今年秋天推出新版本。 绝对正确。我采取了另一种方式,一个接一个地关闭了 viewControllers(这不是解决问题的方法,但它可以工作,而且不是丑陋的)【参考方案3】:

任何想找工作的人都可以这样做:

    用窗口快照覆盖所有内容。 关闭两个没有动画的视图控制器。 在另一个没有动画的视图控制器中显示快照的副本。 移除覆盖窗口的快照。 关闭快照视图控制器动画。

代码如下:

let window = UIApplication.shared.keyWindow!
let snapshot = window.snapshotView(afterScreenUpdates: false)!
window.addSubview(snapshot)

let baseViewController = self.presentingViewController!.presentingViewController!

baseViewController.dismiss(animated: false) 
    let snapshotCopy = snapshot.snapshotView(afterScreenUpdates: false)!
    let snapshotViewController = UIViewController()
    snapshotViewController.view.addSubview(snapshotCopy)

    baseViewController.present(snapshotViewController, animated: false) 
        snapshot.removeFromSuperview()
        baseViewController.dismiss(animated: true, completion: nil)
    

【讨论】:

【参考方案4】:

这里有一个简单的方法可以让你“解雇回家”:

    var vc: UIViewController = self
    while vc.presentingViewController != nil 
        vc = vc.presentingViewController!
    
    vc.dismiss(animated: true, completion: nil)

【讨论】:

【参考方案5】:

您可以在您的 rootViewController 中关闭这些 modalViewControllers。

    UIViewController *viewController = yourRootViewController;

    NSMutableArray *array = [NSMutableArray array];
    while (viewController.modalViewController) 
        [array addObject:viewController];
        viewController = viewController.modalViewController;
    

    for (int i = 0; i < array.count; i++) 
        UIViewController *viewController = array[array.count-1-i];
        [viewController dismissModalViewControllerAnimated:NO];
    

【讨论】:

【参考方案6】:

可以递归找到presentingViewController 到根:

extension UIViewController 
    
    private func _rootPresentingViewController(_ vc:UIViewController, depth:Int) -> UIViewController? 
        guard let parentPresenter = vc.presentingViewController else 
            return vc
        
        if depth > 20 
            return nil
        
        return _rootPresentingViewController(parentPresenter, depth: depth + 1)
    
    
    @objc
    func rootPresentingViewController() -> UIViewController? 
        return _rootPresentingViewController(self, depth: 0)
    
    

【讨论】:

【参考方案7】:

你想要使用的是 popToRootViewControllerAnimated:。它会带您进入根控制器,而不会显示所有介入的控制器。

【讨论】:

如果 A 是 UINavigationController 的根,那将是一个很好的解决方案 哦,是的。我刚醒,所以没听懂。

以上是关于一次关闭多个模态视图控制器?的主要内容,如果未能解决你的问题,请参考以下文章

如何一次关闭 3 个模态视图控制器?

我怎样才能呈现一个模态视图控制器,并带有默认的关闭动画?

模态视图不会关闭

使用多个导航控制器关闭多个模式视图

关闭当前的模态视图控制器,然后呈现新的模态视图控制器

如何关闭模态视图控制器,然后立即让呈现视图控制器呈现不同的模态视图控制器?