iOS7 UIModalTransitionStyleFlipHorizontal转场后会弹跳

73

我正在为iOS 7更新我的应用程序,发现了一个奇怪的问题。我使用UIModalTransitionStyleFlipHorizontal展示了一个封装在UINavigationController中的UIViewController。

iOS 6中它可以很好地工作,但是在iOS 7中,在转换后导航栏会反弹。这与状态栏有关吗?我将主要导航栏的透明度设置为NO

在Info.plist文件中,View controller-based status bar appearance被设置为NO。

以下是演示该问题的最小演示应用程序的GIF:

enter image description here

这是我的代码:

feedNavigationController = [[UINavigationController alloc] init];
feedNavigationController.navigationBar.translucent = NO;

SettingsViewController *settingsVC = [[SettingsViewController alloc] init];

feedNavigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[feedNavigationController setViewControllers:[NSArray arrayWithObjects:settingsVC, nil]];

[self presentViewController:feedNavigationController animated:YES completion:nil];

很遗憾,无法找到该视频。 - Tim Bodeit
完全相同的问题,GM版本存在一些严重问题,其中包括collectionView reloadData! - jasonIM
我已经在问题中添加了一个GIF,展示了在最小演示应用程序中的问题。我已经录制了它以提出同样的问题,但后来发现我不是第一个遇到这个问题的人 :) - Jonathan
FYI:这个 bug 已经在 iOS 8 中修复了。 - Jonathan
1
不是我的问题!iOS 8仍然存在这个问题。在viewwillappear中使用self.navigationController?.navigationBar.layer.removeAllAnimations()修复了它。 - nmdias
1
在我的项目中,我使用 UIView transitionFromView:toView:duration:options:completion: 在 iOS9 上切换两个视图控制器,并遇到了这个 bug。 下面的答案解决了导航栏高度的问题,但没有解决它的色调颜色,有人遇到过这个问题吗? - eagle.dan.1349
6个回答

54

这似乎是一个UIKit的bug。以下解决方法似乎对我有效。

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.navigationController.navigationBar.layer removeAllAnimations];
}

(将此放置在您正在进行转换的视图控制器中。)


这对我有用。请确保在导航控制器的根视图控制器中执行此操作,而不是在导航控制器本身中执行。 - alexcash
同样的情况也会在解散时发生。因此,这只是部分解决了问题。将其添加到开始转换的视图控制器(即从视图控制器)中无法在解散和回转过程中修复此问题。 - Philip
我明白了 - 我从一个没有导航栏的控制器切换,所以从未遇到过这种情况。从技术上讲,它解决了所述的问题 ;) - Ben Packard
它对我起作用了!太棒了!但是当关闭视图控制器时,我们回到的视图控制器会跳动一下。 - HamasN
5
这在第一次调用动画时对我起作用,但在任何后续调用中都没有效果。为什么会这样,因为它每次视图出现时都会被调用???让人发疯。 - Matt Miller
显示剩余7条评论

16

为了解决现在和消失的问题,我使用了iOS7自定义过渡效果。

请将以下代码添加到您的UIViewController中:

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    return (id<UIViewControllerAnimatedTransitioning>)self;
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    return (id<UIViewControllerAnimatedTransitioning>)self;
}

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.7f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    UIView *containerView = [transitionContext containerView];


    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    [containerView addSubview:fromVC.view];

    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    [containerView addSubview:toVC.view];

    UIViewAnimationOptions animationOption = ([toVC.presentedViewController isEqual:fromVC])?UIViewAnimationOptionTransitionFlipFromLeft:UIViewAnimationOptionTransitionFlipFromRight;


    [UIView transitionFromView:fromVC.view
                        toView:toVC.view
                      duration:[self transitionDuration:transitionContext]
                       options:animationOption
                    completion:^(BOOL finished) {
                        [transitionContext completeTransition:YES];
                    }];
}

使用它时,您只需检查是否在iOS7上,并设置transitionDelegate:

YourVCWithTheCustomTransition* yourVC = [[YourVCWithTheCustomTransition alloc] init];

CGFloat deviceVersion = [UIDevice currentDevice].systemVersion.floatValue;
if(deviceVersion >= 7.0) [yourVC setTransitioningDelegate:yourVC];

[self presentModalViewController:yourVC animated:YES];
[yourVC release];
在我的情况下,我有一个自定义的UINavigationController,其中定义了自定义转场:我不必每次都这样做。

在我的情况下,我有一个自定义的UINavigationController,其中定义了自定义转场:我不必每次都这样做。


在设备版本测试中加入“else yourVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;”,就可以美观地运行,并且可以持续在iOS6上运行。 - LavaSlider
这应该是被接受的答案。完美地工作。我唯一的建议是将此代码放入一个单独的类中,然后您可以在任何视图控制器中使用它。 - mluisbrown
3
不要检查系统版本,而是检查方法是否可用:if ([yourVC respondsToSelector:@selector(setTransitioningDelegate:)] { [yourVC setTransitioningDelegate:yourVC]; } - rckoenes
太好了,它可以工作了,我只需要在“viewwillappear”中设置委托,因为我的一些视图是通过故事板编辑器定义的转场。 - Imran
有趣的是,在iOS 8下返回这个转换可能会导致永久的白屏。当然,在iOS 8下你不需要做任何这样的操作,因为导致这种情况的错误已经被修复了。 - user564904

9

这似乎是UIKit的一个bug。以下解决方法似乎对我有效。

presentViewController(将其放置在您要过渡到的视图控制器中):

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.navigationController.navigationBar.layer removeAllAnimations];
}

dismissViewControllerAnimated(放在您要解除显示的视图控制器中):

- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    [self.navigationController.navigationBar.layer removeAllAnimations];
}

如果您不使用自动布局,则需要将以下内容添加到您要dismiss的视图控制器中:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    [self.view setNeedsLayout];
} 

抱歉,上次我粘贴了错误的代码。这段代码对我有效。 - Hugo
这在我的情况下绝对有效。Storyboard 截图:http://cl.ly/image/060t10100b2W - Roger
我可以确认这对我也起作用了。iOS 7.1.1 - AlBeebe

3

我之前也遇到过同样的问题,虽然这不是解决问题的真正方法,但看起来效果还不错。关键在于使用pushViewController/popViewController并结合UIView动画实现翻转效果。以下是一个展示视图控制器的示例代码:

UIViewController *viewController = [[UIViewController alloc] init];
[UIView transitionWithView:self.navigationController.view 
                  duration:0.5 
                   options:UIViewAnimationOptionTransitionFlipFromLeft 
                animations:^{
                   [self.navigationController pushViewController:viewController animated:NO];
                }
                completion:nil];

解散它的方法如下:
[UIView transitionWithView:self.navigationController.view 
                  duration:0.5 
                   options:UIViewAnimationOptionTransitionFlipFromRight 
                animations:^{
                   [self.navigationController popViewControllerAnimated:NO];
                }
                completion:nil];

如果您不希望在推出的控制器上显示 navigationBar,只需在 viewWillAppear 中调用 [self.navigationController setNavigationBarHidden:YES animated:NO] 方法即可。希望这种方法能够帮到您。


1
这实际上解决了过渡的双方的问题,而这不是上面的答案所能做到的。如果您在模态控制器上有一个导航栏,您可能需要将左侧栏按钮项设置为nil,以获得与iOS6相同的效果(无返回按钮),但仅此而已。 - entropy
1
顺便提一下,由于某种原因,这种方法会导致导航栏出现视觉故障(虽然不像不使用它那么严重)。主要表现为标题和返回按钮文本直到过渡后才显示,并在过程中为空白。 - entropy
这应该是被接受的答案,它是一个完美的解决方案,可以在两个方向上进行过渡。只需检查在推送和推送控制器的viewWillAppear方法中隐藏底部栏并根据需要隐藏/显示导航栏即可。 - raladdin

1
对于呈现和被呈现的视图控制器,我有一个UITableViewController,都在UINavigationController中配置,都使用Auto Layout。我注意到其他答案没有解决当tableView的呈现视图控制器在dismiss时会垂直跳动20个点的问题。
这个解决方案解决了这个问题。
在被呈现的视图控制器中(正如Ben Packard所建议的):
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController.navigationBar.layer removeAllAnimations];
}

在呈现视图控制器中(如 dusty 所提出的部分方案):
- (void)viewWillLayoutSubviews{
    if (self.navigationController.presentedViewController) {
        [self.navigationController.navigationBar.layer removeAllAnimations];
        [self.tableView.layer removeAllAnimations];
    }
    [super viewWillLayoutSubviews];
}

-2
对我来说也是一样。实际上有效的方法是将样式更改为CoverVertical,看起来更加流畅。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接