iOS 7 更改 pushviewcontroller 动画方向

6
当我推出一个视图控制器时,动画总是从右到左。我想将该动画更改为相反的从左到右。 我尝试了这段代码,但它不起作用。动画仍然是从右到左。如何简单正确地实现这一点?需要帮助,请提前致谢。
//NOT WORKING
myViewController *mController = [self.storyboard instantiateViewControllerWithIdentifier:@"myViewController"];

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;

[self.view.layer addAnimation:transition forKey:nil];

[self.navigationController pushViewController:mController animated:YES];

尝试使用以下代码:[self.view.window.layer addAnimation:transition forKey:nil]; 而不是 [self.view.layer addAnimation:transition forKey:nil]; - Pintouch
7个回答

11

因为这个问题提到了iOS 7,所以我很惊讶答案没有提到在iOS 7中引入的Animated Transitions API。

如果您想直接进入GitHub,那么objc.io的人有一个很棒的帖子,其中包含一个链接项目here

查看文档,您将看到以下内容:

@availability(iOS, introduced=7.0)
optional func navigationController(navigationController: UINavigationController, 
animationControllerForOperation operation: UINavigationControllerOperation, 
fromViewController fromVC: UIViewController,
 toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?

或者使用Objective-C

- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                   animationControllerForOperation:(UINavigationControllerOperation)operation
                                                fromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);

这意味着从iOS 7开始,您可以控制导航控制器如何实现其推送和弹出动画。

您只需要设置导航控制器的委托并在适当时候返回正确的动画器对象即可。让我们开始吧:

1. 设置UINavigationControllerDelegate

通常我喜欢有一个全局的导航协调器来管理所有的转场等操作。因此,理想情况下,您希望有一个对象能够在NavigationController的整个生命周期内存在。因此,我们可能会有一个类似于以下内容的协调器:

class NavigationCoordinationController: NSObject {
    private(set) var navigationController:UINavigationController        

    required init(navigationController: UINavigationController) {
        self.navigationController = navigationController
        super.init()
        navigationController.delegate = self
    }
}

通常我会在应用程序委托中创建它,以便可以在任何地方引用它。您还可以按视图控制器的要求设置委托,创建自定义的UINavigationController子类或将其设置在任何您认为合适的位置。

2. 创建自定义动画器

现在我们需要一个遵循UIViewControllerAnimatedTransitioning协议的对象。每当需要进行动画转换时,都会调用此对象来应用动画。该示例是一个简单的动画,它会淡化并扩展过渡中的fromView,因此对于推送非常相关。

class CustomPushExpansionTransitioner: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
        return 1
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromView: UIView = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
        let toView: UIView = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
        let container = transitionContext.containerView()
        container.addSubview(toView)
        container.bringSubviewToFront(fromView)

        toView.frame = container.convertRect(fromView.bounds, fromView: fromView)

        UIView.animateWithDuration(transitionDuration(transitionContext),
            animations: { () -> Void in
                fromView.alpha = 0
                fromView.transform = CGAffineTransformConcat(fromView.transform, CGAffineTransformMakeScale(1.3, 1.3))
        }) { (complete) -> Void in
            transitionContext.completeTransition(true)
        }
    }
}

3. 在必要时出售动画师

现在我们需要实现UINavigationController代理,并在需要时出售我们的自定义动画师。

extension NavigationCoordinationController: UINavigationControllerDelegate {
    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        switch operation {
        case .Push:
            return LaunchToHomeTransitioner()
        case .Pop:
            break
        case .None:
            break
        }
        return nil
    }
}

现在您已经完全掌控了您的UINavigationController转换。


你可以在appDelegate中使用required init创建一个UINavigationController对象。但是appDelegate本身并没有UINavigationController对象。你只需要声明这个实例,然后在每个VC中初始化它,并设置各自的View Controller的UINavigationController对象即可。 - topLayoutGuide
@cocotutch,你可以在应用程序委托中创建一个带有根视图控制器的导航控制器,然后实例化协调控制器... 或者你可以子类化导航控制器并成为它的代理。 - Daniel Galasko
我明白了。最终,我声明了变量,并在AppDelegate中基于每个视图控制器将UIViewController导航控制器对象传递给它。因为故事板的缘故,它仍然可以工作。谢谢! - topLayoutGuide
谢谢。找到如何使用pushViewController的Animate Transition确实需要一些时间。 - Artem Illarionov

9

你的做法其实是对的,但根据我从你的代码中理解的情况,你并没有覆盖重写该方法:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated

所以,您需要继承自UINavigationController并覆盖上述方法。
以下是我如何实现(推送+弹出):
推送:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    UIView *theWindow = self.view ;
    if( animated ) {
        CATransition *animation = [CATransition animation];
        [animation setDuration:0.45f];
        [animation setType:kCATransitionPush];
        [animation setSubtype:kCATransitionFromLeft];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [[theWindow layer] addAnimation:animation forKey:@""];
    }

    //make sure we pass the super "animated:NO" or we will get both our
    //animation and the super's animation
    [super pushViewController:viewController animated:NO];

    [self swapButtonsForViewController:viewController];

}

弹出:

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    UIView *theWindow = self.view ;
    if( animated ) {
        CATransition *animation = [CATransition animation];
        [animation setDuration:0.45f];
        [animation setType:kCATransitionPush];
        [animation setSubtype:kCATransitionFromRight];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [[theWindow layer] addAnimation:animation forKey:@""];
    }
    return [super popViewControllerAnimated:NO];
}

谢谢,我会尝试的。 - user3065901

4
NextViewController *next = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];

[UIView transitionWithView:self.navigationController.view 
              duration:0.75 
               options:UIViewAnimationOptionTransitionFlipFromRight 
            animations:^{
             [self.navigationController pushViewController:next animated:NO];
            } 
            completion:nil];

尝试以上代码


1
需要从左侧滑动,而不是翻转。 - Nike Kov

2
你也可以尝试这个方法。
CATransition *transition = [CATransition animation];

transition.duration = 0.5;
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromLeft;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];

DrawingResultsViewController *dr = [DrawingResultsViewController createAndShowProfile:YES];

[self.navigationController pushViewController:dr animated:YES];

0

如果有人在寻找 Xamarin.iOS 上相同的答案(基于 gran33 的回答):

public override void PushViewController(UIViewController viewController, bool animated)
{
    if (animated)
    {
        CATransition animation = new CATransition()
        {
            Duration = 0.5,
            Type = CAAnimation.TransitionPush,
            Subtype = CAAnimation.TransitionFromTop,
            TimingFunction = AMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut)
        };
        View.Layer.AddAnimation(animation, CALayer.Transition);

        base.PushViewController(viewController, false);
    }
}

public override UIViewController PopViewController(bool animated)
{
    if (animated)
    {
        CATransition animation = new CATransition()
        {
            Duration = 0.5,
            Type = CAAnimation.TransitionPush,
            Subtype = CAAnimation.TransitionFromBottom,
            TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut)
            };
            View.Layer.AddAnimation(animation, CALayer.Transition);

            return base.PopViewController(false);
        }
    return base.PopViewController(animated);
}

0
假设myViewController是您的视图控制器,请将自定义转换传递给导航控制器层。
CATransition *transition = [CATransition animation];

transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;

[self.navigationController.view.layer addAnimation:transition forKey:@"pushViewController"]; 

[self.navigationController pushViewController:myViewController animated:NO];

对于RTL语言(例如阿拉伯语),当iOS语言从iPhone / iPad设置更改时,iOS将自动执行此操作。 - Abdurrahman Mubeen Ali
这对我来说可行,但与正常的推送相比,它看起来有点不太好,我在转换过程中得到了一些奇怪的黑色背景,而在正常的推送中则没有。 - TheJeff

0

针对 Swift 4.0+

1 - 创建一个扩展:

extension UINavigationController {
    func customPopView(_ viewController: UIViewController) {
        let transition: CATransition = CATransition()
        transition.duration = 0.3
        transition.type = CATransitionType.push
        transition.subtype = CATransitionSubtype.fromLeft
        view.layer.add(transition, forKey: nil)
        pushViewController(viewController, animated: false)
    }
}

2 - 调用:

self.navigationController?.customPopView(yourViewController)

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