所以有一个堆栈,其中包含三个视图控制器,其中A是根视图控制器,B是第一个模态视图控制器,C是第三个模态视图控制器。我想一次从C到A。我尝试使用此解决方案进行dismiss。它可以起作用,但不是正确的方式。也就是说,当最后一个视图控制器被解除显示时,它会短暂地显示第二个视图控制器,然后再显示第一个视图控制器。我正在寻找的是一种方法,在不注意第二个视图的情况下以一个漂亮的动画从第三个视图控制器到达第一个视图控制器。非常感谢任何对此的帮助。
所以有一个堆栈,其中包含三个视图控制器,其中A是根视图控制器,B是第一个模态视图控制器,C是第三个模态视图控制器。我想一次从C到A。我尝试使用此解决方案进行dismiss。它可以起作用,但不是正确的方式。也就是说,当最后一个视图控制器被解除显示时,它会短暂地显示第二个视图控制器,然后再显示第一个视图控制器。我正在寻找的是一种方法,在不注意第二个视图的情况下以一个漂亮的动画从第三个视图控制器到达第一个视图控制器。非常感谢任何对此的帮助。
请确保只调用一次dismissModalViewControllerAnimated:
。
我发现请求依次关闭每个模态视图控制器会导致两个视图控制器同时执行动画。
假设你有: A =modal> B =modal> C
你应该只调用[myViewControllerA dismissModalViewControllerAnimated:YES]
如果你使用[myViewControllerB dismissModalViewControllerAnimated:YES]
,它将关闭C而不是B。在正常(非嵌套)情况下,它将关闭B(由于响应者链将消息传递到A)。在你描述的嵌套场景中,B是父视图控制器,这比作为模态视图控制器更优先。
对于想要解决此问题的人,你可以这样做:
以下是代码:
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)
}
}
// 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];
}
#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];
}
以下是一种简单的方式,您可以使用它“回到主页”:
var vc: UIViewController = self
while vc.presentingViewController != nil {
vc = vc.presentingViewController!
}
vc.dismiss(animated: true, completion: nil)
你可以在你的rootViewController中dismiss这些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];
}
您可以递归查找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)
}
}
popToRootViewControllerAnimated:
。它可以让你回到根控制器,而不显示所有中间的控制器。UINavigationController
的根视图控制器,那么这是一个很好的解决方案。 - ohhorob