所以有一个包含三个视图控制器的堆栈,其中 A 是根,B 是第一个模态视图控制器,C 是第三个模态 vc。我想立即从 C 转到 A。我已经尝试过这种解决方案来解雇.它确实有效,但方式不正确。也就是说,当最后一个视图控制器被关闭时,它将在显示第一个视图控制器之前简要显示第二个视图控制器。我正在寻找的是一种在一个不错的动画中从第三个 vc 到第一个的方法,而不会注意到第二个视图。对此的任何帮助都非常感谢。
7 回答
确保您只调用dismissModalViewControllerAnimated:
一次。
我发现要求关闭每个堆叠的模态视图控制器会导致它们都动画。
你有:A =modal> B =modal> C
你应该只打电话[myViewControllerA dismissModalViewControllerAnimated:YES]
如果您使用[myViewControllerB dismissModalViewControllerAnimated:YES]
,它将关闭 C,而不是 B。在正常(未堆叠)使用中,它将关闭 B(由于响应者链将消息冒泡到 A)。在您描述的堆叠场景中,B 是父视图控制器,这优先于作为模态视图控制器。
尽管接受的答案确实对我有用,但它现在可能已经过时并且留下了一个看起来很奇怪的动画,其中最上面的模态将立即消失并且动画将在后模态视图上。我尝试了很多方法来避免这种情况,最终不得不使用一些技巧让它看起来不错。注意:(仅在 iOS8+ 中测试,但应该适用于 iOS7+)
基本上, viewControllerA 创建一个UINavigationController
withviewControllerB
作为 rootview 并以模态方式呈现它。
// 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
然后,当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 来添加自动布局约束。您可以修改它以手动添加它们或在 https://github.com/PureLayout/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 级别处理dismissal)
// 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];
}
希望这会有所帮助,快乐的编程:)
对于正在寻找工作的任何人,您都可以这样做:
- 用窗口的快照覆盖所有内容。
- 在没有动画的情况下关闭两个视图控制器。
- 在另一个没有动画的视图控制器中呈现快照的副本。
- 删除覆盖窗口的快照。
- 关闭带有动画的快照视图控制器。
这是代码:
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)
}
}
这是您可以“解雇回家”的一种简单方法:
var vc: UIViewController = self
while vc.presentingViewController != nil {
vc = vc.presentingViewController!
}
vc.dismiss(animated: true, completion: nil)
您可以在您的 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];
}
你可以递归地找到presentingViewController 到root:
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:
. 它让您进入根控制器,而不显示所有介入的控制器。