【问题标题】:Restore pre-iOS7 UINavigationController pushViewController animation恢复 iOS7 之前的 UINavigationController pushViewController 动画
【发布时间】:2013-09-22 22:02:53
【问题描述】:

所以。刚开始将我的 IOS 代码转换到 IOS7,遇到了一些问题。

我有一个UINavigationController,它有子视图控制器,我正在使用pushViewController 来显示下一个视图。要使用一组图像创建视差动画,如果自定义 UINavigationController 以动画一组 UIImageViews 和我的子 ViewControllers 都具有 self.backgroundColor = [UIColor clearColor],透明度。

从 iOS7 开始,UINavController 对其子 vc 的动画方式进行了更新,通过部分移动当前视图控制器并在顶部推动新的视图控制器,我的视差动画看起来很糟糕。我看到之前的 VC 移动了一下,然后消失了。有什么办法可以恢复之前的UINavigationController pushViewController 动画?我似乎在代码中找不到这个。

WelcomeLoginViewController* welcomeLoginViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"WelcomeLogin"];
    [self.navigationController pushViewController:welcomeLoginViewController animated:YES];    

甚至尝试使用:

    [UIView animateWithDuration:0.75
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [self.navigationController pushViewController:welcomeLoginViewController animated:NO];
                         [UIView setAnimationTransition:<specific_animation_form> forView:self.navigationController.view cache:NO];
                     }];

有人知道吗?

【问题讨论】:

  • 为了记录,股票动画(即只是直接推动动画:YES)现在表现出相同的行为。

标签: objective-c animation uinavigationcontroller ios7 pushviewcontroller


【解决方案1】:

我设法通过为UINavigationController 创建一个类别来解决新的过渡类型。就我而言,我需要将其恢复为旧的过渡样式,因为我有透明的 viewControllers 可以在静态背景上滑动。

  • UINavigationController+Retro.h

    @interface UINavigationController (Retro)
    
    - (void)pushViewControllerRetro:(UIViewController *)viewController;
    - (void)popViewControllerRetro;
    
    @end
    
  • UINavigationController+Retro.m

    #import "UINavigationController+Retro.h"
    
    @implementation UINavigationController (Retro)
    
    - (void)pushViewControllerRetro:(UIViewController *)viewController {
        CATransition *transition = [CATransition animation];
        transition.duration = 0.25;
        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        transition.type = kCATransitionPush;
        transition.subtype = kCATransitionFromRight;
        [self.view.layer addAnimation:transition forKey:nil];
    
        [self pushViewController:viewController animated:NO];
    }
    
    - (void)popViewControllerRetro {
        CATransition *transition = [CATransition animation];
        transition.duration = 0.25;
        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        transition.type = kCATransitionPush;
        transition.subtype = kCATransitionFromLeft;
        [self.view.layer addAnimation:transition forKey:nil];
    
        [self popViewControllerAnimated:NO];
    }
    
    @end
    

【讨论】:

  • 您好 Arne,感谢您的解决方案。但是,您为 UINavigationController 的视图而不是控制器的子视图设置动画。这样,导航栏会表现出不同的行为。尝试通过创建两个视图的完全自定义动画来解决,但仍然没有得到想要的效果。
  • 谢谢,这对我有用。但是,当用户点击后退按钮时,它会恢复为被破坏的动画(因为后退按钮不会调用 popViewControllerRetro)。
  • 比尔,你可以用leftBarButtonItem替换后退按钮。我选择了 transition.duration = 0.4 因为它似乎更接近旧的速度,至少在 Xcode 模拟器上是这样。
  • 这对我有用——我还有一个透明背景的视图控制器被推到具有透明背景和背景图像的 NavigationController 上。新的 iOS7 过渡看起来很垃圾,我想知道为什么 Apple 不能只是“默默地”添加这些新功能,而不对原生 UI 行为进行“强制”更改 - 给我们所有人带来了很多麻烦和头痛!
  • 这对我来说非常有用。我在我的 viewControllers 中使用 clearColor(为了在 UIWindow 子视图下获得平滑的动画),但是在实现这个之前得到了一个恼人的随机口吃。
【解决方案2】:

感谢大家的反馈。找到了完全重新创建 UINavigationController 行为的解决方案。当我快要完成时,我遇到了 Nick Lockwood 的解决方案:

https://github.com/nicklockwood/OSNavigationController

OSNavigationController 是 UINavigationController 的开源重新实现。它目前仅具有 UINavigationController 功能的子集,但长期目标是复制 100% 的功能。

OSNavigationController 并不是真的要按原样使用。这个想法是您可以分叉它,然后轻松自定义其外观和行为,以满足您的应用程序可能具有的任何特殊要求。自定义 OSNavigationController 比尝试自定义 UINavigationController 简单得多,因为代码是开放的,您无需担心私有方法、未记录的行为或版本之间的实现更改。

通过用他的代码覆盖我的 UINavigationController,我能够在 UINavigationcontrollers 中处理背景图像

谢谢!

【讨论】:

    【解决方案3】:

    显然,在 iOS7 中,有一种新方法可以定义您自己的自定义 UIViewController 转换。在文档中查找 UIViewControllerTransitioningDelegate。另外,这里有一篇关于它的文章的链接:http://www.doubleencore.com/2013/09/ios-7-custom-transitions/

    【讨论】:

    • 除非我错了,这似乎只适用于呈现视图控制器。将视图控制器推送到导航控制器时,我无法使其工作。
    【解决方案4】:

    找到另一个很好的资源来提供帮助:

    http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions

    使用 iOS7 TLTransitionAnimator 创建自定义动画

    【讨论】:

      【解决方案5】:

      我遇到了一个问题,当 UIViewController A 执行 pushViewController 来推送 UIViewController B 时,推送动画会在大约 25% 处停止,停止,然后在其余部分滑动 B。

      这在 iOS 6 上没有发生,但是当我开始使用 iOS 7 作为 XCode 5 中的基础 SDK 时,这开始发生了。

      解决方法是视图控制器 B 没有在其根视图上设置背景颜色(根视图是 viewController.view 的值,您通常在 loadView 中设置)。在该根视图的初始化程序中设置 backgroundColor 可以解决问题。

      我设法解决了这个问题:

      // CASE 1: The root view for a UIViewController subclass that had a halting animation
      
      - (id)initWithFrame:(CGRect)frame
      
      {
      
           if ((self = [super initWithFrame:frame])) {
      
                // Do some initialization ...
      
                // self.backgroundColor was NOT being set
      
                // and animation in pushViewController was slow and stopped at 25% and paused
      
           }
      
           return self;
      
      }
      
      // CASE 2: HERE IS THE FIX
      
      - (id)initWithFrame:(CGRect)frame
      
      {
      
           if ((self = [super initWithFrame:frame])) {
      
                // Do some initialization ...
      
                // Set self.backgroundColor for the fix!
      
                // and animation in pushViewController is no longer slow and and no longer stopped at 25% and paused
      
                self.backgroundColor = [UIColor whiteColor]; // or some other non-clear color
      
           }
      
           return self;
      
      }
      

      【讨论】:

      • 我遇到了完全相同的问题,但不幸的是我的视图 B 有 clearColor,我只需要添加与 self.parentViewController.view.xxxxxx 完全相同的图像背景,现在它就可以工作了。 .. 谢谢!
      • 是的,这完全解决了问题。谢谢!
      • 疯狂的解决方案。也为我工作!
      • 你是怎么知道的?我本可以尝试 Voodoo 来代替..谢谢
      【解决方案6】:

      首先,我没有使用 Storyboard。我尝试使用 UINavigationController+Retro。出于某种原因,UINavigationController 很难在堆栈顶部释放 UIViewController。这是使用 iOS 7 自定义过渡对我有用的解决方案。

      1. 将委托设置为自己。

        navigationController.delegate = self;
        
      2. 声明这个 UINavigationControllerDelegate。

        - (id<UIViewControllerAnimatedTransitioning>)navigationController (UINavigationController *)navigationController 
        animationControllerForOperation:(UINavigationControllerOperation)operation
        fromViewController:(UIViewController *)fromVC 
        toViewController:(UIViewController *)toVC 
        {
            TransitionAnimator *animator = [TransitionAnimator new]; 
            animator.presenting = YES;
            return animator;
        }
        

        请注意,只有在动画设置为 YES 时才会调用它。例如

        [navigationController pushViewController:currentViewController animated:YES];
        
      3. 创建扩展 NSObject 的动画师类。我叫我的TransitionAnimator,它是从UIViewController-Transitions-Example里面的TeehanLax的TLTransitionAnimator修改而来的。

        TransitionAnimator.h

        #import <Foundation/Foundation.h>
        @interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
        @property (nonatomic, assign, getter = isPresenting) BOOL presenting;
        @end
        

        TransitionAnimator.m

        #import "TransitionAnimator.h"
        @implementation TransitionAnimator
        - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
            return 0.5f;
        }
        - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
            UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
            UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];    
            if (self.presenting) {
                //ANIMATE VC ENTERING FROM THE RIGHT SIDE OF THE SCREEN
                [transitionContext.containerView addSubview:fromVC.view];
                [transitionContext.containerView addSubview:toVC.view];     
                toVC.view.frame = CGRectMake(0, 0, 2*APP_W0, APP_H0); //SET ORIGINAL POSITION toVC OFF TO THE RIGHT                
                [UIView animateWithDuration:[self transitionDuration:transitionContext] 
                    animations:^{
                        fromVC.view.frame = CGRectMake(0, (-1)*APP_W0, APP_W0, APP_H0); //MOVE fromVC OFF TO THE LEFT
                        toVC.view.frame = CGRectMake(0, 0, APP_W0, APP_H0); //ANIMATE toVC IN TO OCCUPY THE SCREEN
                    } completion:^(BOOL finished) {
                        [transitionContext completeTransition:YES];
                    }];
            }else{
                //ANIMATE VC EXITING TO THE RIGHT SIDE OF THE SCREEN
            }
        }
        @end
        

        使用呈现标志设置您想要动画的方向或您喜欢的任何条件。这是苹果参考的link

      【讨论】:

      • 太棒了。一件事是不需要使用呈现变量。 UINavigationControllerOperation 表示此操作是 Push 或 Pop。
      【解决方案7】:

      我投票支持@Arne 的答案,因为我发现它是解决这个问题的最优雅的解决方案。我只想添加一些代码,以便从他对@Arne 解决方案的评论中回答@Bill 的问题。以下是评论引述:

      谢谢,这对我有用。但是,当用户点击 Back 按钮,它恢复到破坏动画(因为后退按钮 不调用 popViewControllerRetro)。 – 比尔 10 月 3 日 12:36

      为了在按下后退按钮时调用 popViewControllerRetro,您可以执行一个小技巧来实现此目的。进入你的推送视图控制器,导入 UIViewController+Retro.h 并将这段代码添加到你的 viewWillDisappear 方法中:

      - (void)viewWillDisappear:(BOOL)animated {
          if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
              [self.navigationController popViewControllerRetro];
          }
      
          [super viewWillDisappear:animated];
      }
      

      此 if 语句将检测何时按下后退按钮并从类别类调用 popViewControllerRetro。

      最好的问候。

      【讨论】:

        【解决方案8】:

        我对清晰的背景颜色和糟糕的动画也有同样的问题,所以我使用新的 iOS7 API 为 ViewController 创建了自定义过渡。您所需要的只是为您的导航控制器设置一个委托:

        // NavigationController does not retain delegate, so you should hold it.
        self.navigationController.delegate = self.navigationTransitioningDelegate;
        

        只需将此文件添加到您的项目中:MGNavigationTransitioningDelegate

        【讨论】:

        • 太棒了!最佳解决方案
        • 像魅力一样工作!谢谢!
        • 非常有用!谢谢!
        • 不幸的是,此解决方案破坏了自动布局 - 当您旋转并返回时,上一页上的视图全部中断,直到您再次旋转。
        • 很好的解决方案。我制作了你的要点的 Swift 3 版本:gist.github.com/steryokhin/f136bd3e0c38b0fcdbff30b311714257
        【解决方案9】:

        只需添加:

        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        

        这个:

        [[self window] setBackgroundColor:[UIColor whiteColor]];
        

        最终结果:

        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {    
            [[self window] setBackgroundColor:[UIColor whiteColor]];
        
            // Override point for customization after application launch.
            return YES;
        }
        

        【讨论】:

          【解决方案10】:

          Arne 回答的 Swift 5 实现:

          extension UINavigationController {
            func pushViewControllerLegacyTransition(_ viewController: UIViewController) {
              let transition = CATransition()
              transition.duration = 0.25
              transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
              transition.type = .push
              transition.subtype = .fromRight
              view.layer.add(transition, forKey: nil)
              pushViewController(viewController, animated: false)
            }
          
            func popViewControllerLegacyTransition() {
              let transition = CATransition()
              transition.duration = 0.25
              transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
              transition.type = .push
              transition.subtype = .fromLeft
              view.layer.add(transition, forKey: nil)
              popViewController(animated: false)
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-04-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-11-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多