关闭堆栈中较低的ViewController不会按预期运行 [英] Dismissing a ViewController lower in the stack does not behave as expected

查看:89
本文介绍了关闭堆栈中较低的ViewController不会按预期运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个复杂的应用程序,它在中间有一个分支。



在应用程序的某个时刻,会出现一个特定的UIViewController,我们' ll称之为 mainViewController (缩短 mainVC )。



mainVC 使用以下代码按代码显示另一个视图控制器(出于隐私原因我删除了部分视图):

  UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@SecondaryStoryboardbundle:secondaryBundle]; 
SecondViewController * secondVC = [storyboard instantiateInitialViewController];
[self presentViewController:secondVC animated:YES completion:nil];

所以 secondVC 稍后会出现另一个视图控制器,名为 thirdVC 。这是使用自定义segue完成的,在上面的代码中使用的故事板中设置,代码如下所示:

  @implementation VCCustomPushSegue 

- (void)执行{

UIView * sourceView =((UIViewController *)self.sourceViewController).view;
UIView * destinationView =((UIViewController *)self.destinationViewController).view;

UIWindow * window = [[[UIApplication sharedApplication] delegate] window];
destinationView.center = CGPointMake(sourceView.center.x + sourceView.frame.size.width,destinationView.center.y);

[window insertSubview:destinationView aboveSubview:sourceView];

[UIView animateWithDuration:0.4
动画:^ {
destinationView.center = CGPointMake(sourceView.center.x,destinationView.center.y);
sourceView.center = CGPointMake(0 - sourceView.center.x,destinationView.center.y);
}
完成:^(BOOL完成){

[self.sourceViewController presentViewController:self.destinationViewController animated:NO completion:nil];
}];

}

@end

当你可以看到这个segue使用自定义动画(从右到左的幻灯片)以模态方式(通过使用 presentViewController:)呈现目标视图控制器。



所以基本上到这里一切都很好。我使用经典模态动画(从底部向上滑动)呈现 secondVC ,并使用我的自定义转换显示 thirdVC



但是当我想要解雇 thirdVC 时,我想要的是直接回到 mainVC 。所以我从 thirdVC 中调用以下内容:

  self.modalTransitionStyle = UIModalTransitionStyleCoverVertical; 
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:_animate completion:nil];

这样,我正在调用 dismissViewControllerAnimated:直接在 mainVC (由 self.presentingViewController.presentingViewController 引用),我期待 thirdVC 将被动画解雇, secondVC 将在没有动画的情况下消失。



正如Apple在UIViewController类文档中所说:


呈现视图控制器负责解除视图$ b它呈现的$ b控制器。如果您在呈现的视图
控制器本身上调用此方法,它会自动将消息转发给
呈现视图控制器。



如果你连续出现了几个视图控制器,从而构建了一个
的呈现视图控制器堆栈,在视图上调用此方法
控制器在堆栈中较低的位置解除其直接子视图
控制器和堆栈上该子项上方的所有视图控制器

发生这种情况时,只有最顶层的视图才能以动画
方式解散
;只需从
堆栈中删除任何中间视图控制器。最顶层的视图使用其模态转换
样式被解除,这可能与其他视图控制器
在堆栈中使用的样式不同。


问题在于它不会发生什么。在我的场景中, thirdVC 消失,并显示 secondVC 被解除,使用经典模态幻灯片到底部动画。 / p>

我做错了什么?






编辑:



所以@codeFi的答案可能在一个经典项目中有效,但问题在于我正在研究一个框架。所以 mainVC 将在客户端应用程序中, secondVC thirdVC 在我的框架中,在一个单独的故事板中。除了在我的代码中对它的引用之外,我无法以任何其他方式访问 mainVC ,因此很遗憾,unwind segues不是一个选项。

解决方案

我一直有这个完全相同的问题,我已经成功地通过添加屏幕快照作为子视图来解决它 secondVC.view ,如下所示:

  if(self.presentedViewController。 presentsViewController){
[self.presentedViewController.view addSubview:[[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]];
}

[self dismissViewControllerAnimated:YES completion:nil];

不漂亮,但似乎有效。



注意:如果您的 secondVC 有一个导航栏,则需要在快照屏幕和添加导航栏之间隐藏导航栏快照作为 secondVC 的子视图,否则快照将显示在导航栏下方,因此在解雇动画期间似乎显示双导航栏。代码:

  if(self.presentedViewController.presentedViewController){
UIView * snapshot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:没有];
[self.presentedViewController.navigationController setNavigationBarHidden:YES animated:NO];
[self.presentedViewController.view addSubview:snapshot];
}

[self dismissViewControllerAnimated:YES completion:nil];


I'm building a complex app that has kind of a branch in the middle.

At some point in the app, a particular UIViewController is presented, we'll call it mainViewController (shortened mainVC).

The mainVC presents another view controller, by code, using the following code (I strip out parts of it for privacy reasons):

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"SecondaryStoryboard" bundle:secondaryBundle];
SecondViewController *secondVC = [storyboard instantiateInitialViewController];
[self presentViewController:secondVC animated:YES completion:nil];

So the secondVC will later present another view controller, called thirdVC. This is done using a custom segue, set in the storyboard used in the code above, which code looks like this:

@implementation VCCustomPushSegue

- (void)perform {

    UIView *sourceView = ((UIViewController *)self.sourceViewController).view;
    UIView *destinationView = ((UIViewController *)self.destinationViewController).view;

    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
    destinationView.center = CGPointMake(sourceView.center.x + sourceView.frame.size.width, destinationView.center.y);

    [window insertSubview:destinationView aboveSubview:sourceView];

    [UIView animateWithDuration:0.4
                     animations:^{
                         destinationView.center = CGPointMake(sourceView.center.x, destinationView.center.y);
                         sourceView.center = CGPointMake(0 - sourceView.center.x, destinationView.center.y);
                     }
                     completion:^(BOOL finished){

                         [self.sourceViewController presentViewController:self.destinationViewController animated:NO completion:nil];
                     }];

}

@end

As you can see this segue presents the destination view controller modally (by the use of presentViewController:) with a custom animation (a slide from right to left).

So basically up to here everything is fine. I present the secondVC with a classic modal animation (slide up from bottom) and present the thirdVC with my custom transition.

But when I want to dismiss the thirdVC, what I want is to go back directly to the mainVC. So I call the following from the thirdVC :

self.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:_animate completion:nil];

That way, I'm calling dismissViewControllerAnimated: directly on mainVC (referenced by self.presentingViewController.presentingViewController), and I'm expecting the thirdVC to be dismissed with an animation, and the secondVC to just disappear without animation.

As Apple says in the UIViewController Class Documentation:

The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.

If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

The issue is that it's not what happens. In my scenario, the thirdVC disappears, and shows the secondVC being dismissed with the classic modal slide to bottom animation.

What am I doing wrong ?


Edit :

So @codeFi's answer is probably working in a classic project, but the problem here is that I'm working on a framework. So mainVC would be in a client app, and the secondVC and thirdVC are in my framework, in a separate storyboard. I don't have access to mainVC in any other way than a reference to it in my code, so unwind segues are unfortunately not an option here.

解决方案

I've been having this exact same issue, and I've managed to visually work around it by adding a snapshot of the screen as a subview to secondVC.view, like so:

if (self.presentedViewController.presentedViewController) {
    [self.presentedViewController.view addSubview:[[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]];
}

[self dismissViewControllerAnimated:YES completion:nil];

Not pretty, but it seems to be working.

NOTE: if your secondVC has a navigation bar, you will need to hide the navigation bar in between snapshotting the screen and adding the snapshot as a subview to secondVC, as otherwise the snapshot will appear below the navigation bar, thus seemingly displaying a double navigation bar during the dismissal animation. Code:

if (self.presentedViewController.presentedViewController) {
    UIView *snapshot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
    [self.presentedViewController.navigationController setNavigationBarHidden:YES animated:NO];
    [self.presentedViewController.view addSubview:snapshot];
}

[self dismissViewControllerAnimated:YES completion:nil];

这篇关于关闭堆栈中较低的ViewController不会按预期运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆