如何在视图控制器之间实现淡入淡出/无过渡效果

63

在Storyboard中的视图控制器之间是否可以进行淡入淡出过渡?或者不使用过渡效果。

如果有可能,那么它的代码是什么?

11个回答

145

如果呈现模态视图控制器,你可以指定一个 modalTransitionStyle 属性为 UIModalTransitionStyleCrossDissolve。如果在你的故事板中使用 Segue 实现此操作,请选择 Segue 的属性检查器,并在那里指定过渡样式:

输入图像描述

如果以编程方式呈现视图控制器,你可以在故事板中为视图控制器之间定义带有“交叉溶解”效果的 modal segue,然后让源视图控制器执行此 segue 操作:

[self performSegueWithIdentifier:@"presentSegue" sender:sender];

或者,如果你正在调用 presentViewController

UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"YourStoryboardID"];
controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:controller animated:YES completion:nil];
在iOS 7中,苹果提供了一项新技术,为高度自定义的转换提供了丰富而强大的控制。欲了解更多信息,请参考WWDC 2013视频Custom Transitions Using View Controllers。 但是,例如,如果您想在iOS 7中自定义推入和弹出动画以进行淡入淡出,您需要为导航控制器指定一个delegate
- (void)viewDidLoad {
    [super viewDidLoad];

    self.navigationController.delegate = self;
}

然后您需要实现animationControllerForOperation方法,该方法指定了推入和弹出的动画器对象:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController*)fromVC
                                                 toViewController:(UIViewController*)toVC
{
    if (operation == UINavigationControllerOperationPush)
        return [[PushAnimator alloc] init];

    if (operation == UINavigationControllerOperationPop)
        return [[PopAnimator alloc] init];

    return nil;
}

你显然需要实现自定义的push和pop动画,例如:

@interface PushAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@end

@implementation PushAnimator

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

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    [[transitionContext containerView] addSubview:toViewController.view];

    toViewController.view.alpha = 0.0;

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        toViewController.view.alpha = 1.0;
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end

并且

@interface PopAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@end

@implementation PopAnimator

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

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromViewController.view.alpha = 0.0;
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end

还有一种类似但略有不同的技巧可以定制模态转场效果(但如果你只是在做一个界面,你可能会使用上文提到的modalTransitionStyle,除非你想采用其他微妙的自定义)。更多信息,请参见前面提到的使用视图控制器进行自定义转场视频。

总之,iOS 7的自定义转场是一种稍微复杂但非常强大的方式,可以极大地控制转场动画。


6
感谢您提供如此详尽且可复制粘贴的回答! - xaphod

16
创建自定义Segue,需要创建UIStoryboard Segue的子类。例如:
// MCFadeSegue.h
#import <UIKit/UIKit.h>

@interface MCFadeSegue : UIStoryboardSegue

@end

// MCFadeSegue.m
#import <QuartzCore/QuartzCore.h>
#import "MCFadeSegue.h"

@implementation MCFadeSegue

- (void)perform
{
  CATransition *transition = [CATransition animation];
  transition.duration = 0.5;
  transition.type = kCATransitionFade;

  [[[[[self sourceViewController] view] window] layer] addAnimation:transition
      forKey:kCATransitionFade];

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

@end

然后在 MainStoryboard.storyboard 中选择 segue,将 Style 设置为 Custom,Class 设置为 MCFadeSegue。


应该是 @implementation MCFadeSegue 而不是 @implementation MCFadeInSegue,.h 文件也一样。 - brian.clear

9

在Swift中实现Push/Pop UIViewController渐变淡入淡出效果

class FadeInPushSegue: UIStoryboardSegue {

    var animated: Bool = true

    override func perform() {

        if var sourceViewController = self.sourceViewController as? UIViewController, var destinationViewController = self.destinationViewController as? UIViewController {

            var transition: CATransition = CATransition()

            transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
            sourceViewController.view.window?.layer.addAnimation(transition, forKey: "kCATransition")
            sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)


        }
    }

}

class FadeOutPopSegue: UIStoryboardSegue {

    override func perform() {

        if var sourceViewController = self.sourceViewController as? UIViewController, var destinationViewController = self.destinationViewController as? UIViewController {

            var transition: CATransition = CATransition()

            transition.duration = 0.4
            transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
            transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade

            sourceViewController.view.window?.layer.addAnimation(transition, forKey: "kCATransition")
            sourceViewController.navigationController?.popViewControllerAnimated(false)
        }
    }

}

8
试试这个。
    let transition: CATransition = CATransition()
    transition.duration = 0.4
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.type = kCATransitionFade
    self.navigationController!.view.layer.addAnimation(transition, forKey: nil)

    let vc = self.storyboard?.instantiateViewControllerWithIdentifier("vcID") as! My_ViewController
    self.navigationController?.pushViewController(vc, animated: false)

非常好的答案。在长时间的搜索之后,它对我有很大的帮助。谢谢。 - Wahib

6

不需要创建自定义segue,我编写了以下代码以呈现视图...

UIViewController *nextView = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"YOUR_VIEW_CONTROLLER_STORYBOARD_NAME"];
nextView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentViewController:nextView animated:YES completion:nil]; //(要使用交叉溶解,请设置animated:YES。对于没有过渡效果,请设置animated:NO。)

希望这对任何遇到此问题的人有所帮助!


4

我的Swift版本支持可选项检查!

    let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
    if let stView = storyboard.instantiateViewControllerWithIdentifier("STVC") as? STVC {
        stView.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
        self.navigationController?.presentViewController(stView, animated: true, completion: nil)
    }

只需确保在IB中设置“视图控制器”的Storyboard ID。

3

基于 Eugene Braginets答案,在 Swift 3 语法中推送/弹出 UIViewController 并实现淡入淡出效果。

class FadeInPushSegue: UIStoryboardSegue {

    var animated: Bool = true

    override func perform() {

        let sourceViewController = self.source
        let destinationViewController = self.destination

        let transition: CATransition = CATransition()

        transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
        sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
        sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)

    }

}

class FadeOutPopSegue: UIStoryboardSegue {

    override func perform() {

        let sourceViewController = self.source

        let transition: CATransition = CATransition()

        transition.duration = 0.4
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade

        sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
        _ = sourceViewController.navigationController?.popViewController(animated: false)
    }

}

3

Swift 5:

花费了数小时寻找正确的解决方案后,Rob提供的一个解决方案符合我的所有需求,在进行了一些调整后,它变得完美无缺。

创建淡入淡出视图控制器类:

class FadeViewControllerTransition: NSObject, UIViewControllerAnimatedTransitioning {
    
    var fadeOut: Bool = false
    
    func transitionDuration(
        using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        if fadeOut {
            animateFadeOut(using: transitionContext)
            return
        }
        
        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
        transitionContext.containerView.addSubview(toViewController.view)
        toViewController.view.alpha = 0.0
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
            toViewController.view.alpha = 1.0
        }, completion: { _ in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })
    }
    
    func animateFadeOut(using transitionContext: UIViewControllerContextTransitioning) {
        
        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
        let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
        transitionContext.containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view)
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
            fromViewController.view.alpha = 0.0
        }, completion: { _ in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })
    }
}

在您的导航控制器的第一个视图控制器中:
class MainViewController: UIViewController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {

    private let fadeVCTransition = FadeViewControllerTransition()
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        fadeVCTransition.fadeOut = (operation == .pop)
        return fadeVCTransition
    }

我知道了。


我该如何使用fadeVCTransition变量和navigationController函数来执行淡入转场呢? - Wahib

2
我所做的是在Interface Builder中使用Storyboard,连接需要移动到/从原视图控制器和目标视图控制器(按住ctrl并从原视图控制器向目标视图控制器拖动单击),并更改了segue的属性。我使用了以下内容:

enter image description here

我接着使用了以下内容:
[self performSegueWithIdentifier:@"showMovieDetailSegue" sender:self];

当你想要移除它时,只需从原始视图控制器中调用此代码:
[self dismissViewControllerAnimated:YES completion:nil];

简单而非常快速。

1
这让我疯狂了几个小时。这些解决方案似乎都不是我想要的,我都快疯了。最终,我想到了以下方法,使用淡出/淡入过渡在单个NSWindow中交换视图控制器。首先执行淡出,然后在完成处理程序中将窗口的视图设置为新视图并执行淡入。也许有更好的方法来做到这一点,但这是我能够拼凑出的最简单的方法。
class CustomSegue : NSStoryboardSegue {
    override func perform() {
        guard let fromController = sourceController as? NSViewController else {
            return;
        }
        guard let toController = destinationController as? NSViewController else {
            return;
        }
        let animationDuration = 0.5;
        NSAnimationContext.runAnimationGroup({
            $0.duration = animationDuration;
            fromController.view.animator().alphaValue = 0.0;
        }, completionHandler: {
            fromController.view.window?.contentViewController = toController;
            toController.view.alphaValue = 0.0;
            NSAnimationContext.runAnimationGroup({
                $0.duration = animationDuration;
                toController.view.animator().alphaValue = 1.0;
            })
        });

    }
}

苹果文档中缺乏示例让我感到非常头疼,而且到处充斥着Objective C和Swift的混合使用,实在是太糟糕了!

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