一次解除多个模态视图控制器?

时间:2010-07-12 15:28:25

标签: iphone uiviewcontroller modal-dialog dismiss

所以有一个带有三个视图控制器的堆栈,其中A是root,B是第一个模态视图控制器,C是第三个模态vc。我想立刻从C到A。我尝试了this solution to dismiss。它工作但不是以正确的方式。也就是说,当最后一个视图控制器被解除时,它将在显示第一个视图控制器之前完全显示第二个视图控制器。我正在寻找的是一种从第三个vc到第一个vc的方法在一个不错的动画中没有注意到第二个视图。对此的任何帮助都非常有用。

7 个答案:

答案 0 :(得分:22)

请确保您只拨打dismissModalViewControllerAnimated:一次。

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

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

您应该只拨打[myViewControllerA dismissModalViewControllerAnimated:YES]

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

答案 1 :(得分:4)

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

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

// 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,但在我们展示之后,我们将在{viewControllerC上放置viewControllerB的快照。 1}}的导航控制器。然后,当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];
}

希望这会有所帮助,快乐编程:)

答案 2 :(得分:2)

对于任何寻找工作的人,都可以这样做:

  1. 用窗口快照覆盖所有内容。
  2. 在没有动画的情况下关闭两个视图控制器。
  3. 在没有动画的情况下在另一个视图控制器中显示快照的副本。
  4. 删除窗口上的快照。
  5. 使用动画关闭快照视图控制器
  6. 以下是代码:

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

答案 3 :(得分:0)

您可以在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];
    }

答案 4 :(得分:0)

您可以递归地找到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)
    }
    
}

答案 5 :(得分:0)

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

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

答案 6 :(得分:-5)

您想要使用的是 popToRootViewControllerAnimated:。它将您带到根控制器,而不显示所有干预的。