Swift中的自定义翻转序列

Swift中的自定义翻转序列,swift,xcode6,Swift,Xcode6,这是我的自定义序列代码 class FlipFromRightSegue: UIStoryboardSegue { override func perform() { let source:UIViewController = self.sourceViewController as UIViewController let destination:UIViewController = self.destinationViewController as UIViewControl

这是我的自定义序列代码

class FlipFromRightSegue: UIStoryboardSegue {
override func perform() {
    let source:UIViewController = self.sourceViewController as UIViewController
    let destination:UIViewController = self.destinationViewController as UIViewController

    UIView.transitionWithView(source.view, duration: 1.0, options: .CurveEaseInOut | .TransitionFlipFromRight, animations: { () -> Void in
        source.view.addSubview(destination.view)
    }) { (finished) -> Void in
        destination.view.removeFromSuperview()
        source.presentViewController(destination, animated: false, completion: nil)
    }
}
}
我认为这是可行的,但实际上只有当segue已经执行时,视图才会改变。当“翻转”在中间时,我应该怎么做才能使视图改变?


提前感谢。

从iOS 7开始,我们通常不使用自定义序列设置过渡动画。我们要么使用标准的模态表示,指定一个
模态转换样式
(即,我们可以为模态转换选择一些动画的固定列表),要么实现自定义动画转换。这两个方面的描述如下:

  • 如果只是显示另一个视图控制器的视图,将动画更改为翻转的简单解决方案是在目标视图控制器中设置
    modaltransationstyle
    。您可以在segue属性下的Interface Builder中完全执行此操作

    如果希望以编程方式执行此操作,则可以在目标控制器中使用Swift 3执行以下操作:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        modalTransitionStyle = .flipHorizontal   // use `.FlipHorizontal` in Swift 2
    }
    
    class SecondViewController: UIViewController {
    
        let customTransitionDelegate = TransitioningDelegate()
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            modalPresentationStyle = .custom   // use `.Custom` in Swift 2
            transitioningDelegate = customTransitionDelegate
        }
    
        ...
    }
    
    class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .presenting)
        }
    
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .dismissing)
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        var animationTransitionOptions: UIViewAnimationOptions
    
        init(transitionType: TransitionType) {
            switch transitionType {
            case .presenting:
                animationTransitionOptions = .transitionFlipFromRight
            case .dismissing:
                animationTransitionOptions = .transitionFlipFromLeft
            }
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            //let inView   = transitionContext.containerView
            let toView   = transitionContext.viewController(forKey: .to)?.view
            let fromView = transitionContext.viewController(forKey: .from)?.view
    
            UIView.transition(from: fromView!, to: toView!, duration: transitionDuration(using: transitionContext), options: animationTransitionOptions.union(.curveEaseInOut)) { finished in
                transitionContext.completeTransition(true)
            }
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        let transitionType: TransitionType
    
        init(transitionType: TransitionType) {
            self.transitionType = transitionType
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let inView   = transitionContext.containerView
            let toView   = transitionContext.view(forKey: .to)!
            let fromView = transitionContext.view(forKey: .from)!
    
            var frame = inView.bounds
    
            func flipTransform(angle: CGFloat, offset: CGFloat = 0) -> CATransform3D {
                var transform = CATransform3DMakeTranslation(offset, 0, 0)
                transform.m34 = -1.0 / 1600
                transform = CATransform3DRotate(transform, angle, 0, 1, 0)
                return transform
            }
    
            toView.frame = inView.bounds
            toView.alpha = 0
    
            let transformFromStart:  CATransform3D
            let transformFromEnd:    CATransform3D
            let transformFromMiddle: CATransform3D
            let transformToStart:    CATransform3D
            let transformToMiddle:   CATransform3D
            let transformToEnd:      CATransform3D
    
            switch transitionType {
            case .presenting:
                transformFromStart  = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: -.pi / 2)
                transformToStart    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: .pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
    
            case .dismissing:
                transformFromStart  = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: .pi / 2)
                transformToStart    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: -.pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
            }
    
            toView.layer.transform = transformToStart
            fromView.layer.transform = transformFromStart
            inView.addSubview(toView)
    
            UIView.animateKeyframes(withDuration: self.transitionDuration(using: transitionContext), delay: 0, options: [], animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.0) {
                    toView.alpha = 0
                    fromView.alpha = 1
                }
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                    toView.layer.transform = transformToMiddle
                    fromView.layer.transform = transformFromMiddle
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0) {
                    toView.alpha = 1
                    fromView.alpha = 0
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                    toView.layer.transform = transformToEnd
                    fromView.layer.transform = transformFromEnd
                }
            }, completion: { finished in
                toView.layer.transform = CATransform3DIdentity
                fromView.layer.transform = CATransform3DIdentity
                toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    然后,当您调用
    show
    /
    showViewController
    present
    /
    presentViewController
    时,您的演示将以水平翻转方式执行。并且,当您关闭视图控制器时,动画将自动为您反转

  • 如果您需要更多的控制,在iOS 7及更高版本中,您可以使用自定义动画转换,在其中您可以指定
    modalPresentationStyle
    .custom
    。例如,在Swift 3中:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        modalTransitionStyle = .flipHorizontal   // use `.FlipHorizontal` in Swift 2
    }
    
    class SecondViewController: UIViewController {
    
        let customTransitionDelegate = TransitioningDelegate()
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            modalPresentationStyle = .custom   // use `.Custom` in Swift 2
            transitioningDelegate = customTransitionDelegate
        }
    
        ...
    }
    
    class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .presenting)
        }
    
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .dismissing)
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        var animationTransitionOptions: UIViewAnimationOptions
    
        init(transitionType: TransitionType) {
            switch transitionType {
            case .presenting:
                animationTransitionOptions = .transitionFlipFromRight
            case .dismissing:
                animationTransitionOptions = .transitionFlipFromLeft
            }
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            //let inView   = transitionContext.containerView
            let toView   = transitionContext.viewController(forKey: .to)?.view
            let fromView = transitionContext.viewController(forKey: .from)?.view
    
            UIView.transition(from: fromView!, to: toView!, duration: transitionDuration(using: transitionContext), options: animationTransitionOptions.union(.curveEaseInOut)) { finished in
                transitionContext.completeTransition(true)
            }
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        let transitionType: TransitionType
    
        init(transitionType: TransitionType) {
            self.transitionType = transitionType
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let inView   = transitionContext.containerView
            let toView   = transitionContext.view(forKey: .to)!
            let fromView = transitionContext.view(forKey: .from)!
    
            var frame = inView.bounds
    
            func flipTransform(angle: CGFloat, offset: CGFloat = 0) -> CATransform3D {
                var transform = CATransform3DMakeTranslation(offset, 0, 0)
                transform.m34 = -1.0 / 1600
                transform = CATransform3DRotate(transform, angle, 0, 1, 0)
                return transform
            }
    
            toView.frame = inView.bounds
            toView.alpha = 0
    
            let transformFromStart:  CATransform3D
            let transformFromEnd:    CATransform3D
            let transformFromMiddle: CATransform3D
            let transformToStart:    CATransform3D
            let transformToMiddle:   CATransform3D
            let transformToEnd:      CATransform3D
    
            switch transitionType {
            case .presenting:
                transformFromStart  = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: -.pi / 2)
                transformToStart    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: .pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
    
            case .dismissing:
                transformFromStart  = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: .pi / 2)
                transformToStart    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: -.pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
            }
    
            toView.layer.transform = transformToStart
            fromView.layer.transform = transformFromStart
            inView.addSubview(toView)
    
            UIView.animateKeyframes(withDuration: self.transitionDuration(using: transitionContext), delay: 0, options: [], animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.0) {
                    toView.alpha = 0
                    fromView.alpha = 1
                }
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                    toView.layer.transform = transformToMiddle
                    fromView.layer.transform = transformFromMiddle
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0) {
                    toView.alpha = 1
                    fromView.alpha = 0
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                    toView.layer.transform = transformToEnd
                    fromView.layer.transform = transformFromEnd
                }
            }, completion: { finished in
                toView.layer.transform = CATransform3DIdentity
                fromView.layer.transform = CATransform3DIdentity
                toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    它指定将实例化动画控制器的
    UIViewControllerTransitioningDelegate
    。例如,在Swift 3中:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        modalTransitionStyle = .flipHorizontal   // use `.FlipHorizontal` in Swift 2
    }
    
    class SecondViewController: UIViewController {
    
        let customTransitionDelegate = TransitioningDelegate()
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            modalPresentationStyle = .custom   // use `.Custom` in Swift 2
            transitioningDelegate = customTransitionDelegate
        }
    
        ...
    }
    
    class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .presenting)
        }
    
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .dismissing)
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        var animationTransitionOptions: UIViewAnimationOptions
    
        init(transitionType: TransitionType) {
            switch transitionType {
            case .presenting:
                animationTransitionOptions = .transitionFlipFromRight
            case .dismissing:
                animationTransitionOptions = .transitionFlipFromLeft
            }
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            //let inView   = transitionContext.containerView
            let toView   = transitionContext.viewController(forKey: .to)?.view
            let fromView = transitionContext.viewController(forKey: .from)?.view
    
            UIView.transition(from: fromView!, to: toView!, duration: transitionDuration(using: transitionContext), options: animationTransitionOptions.union(.curveEaseInOut)) { finished in
                transitionContext.completeTransition(true)
            }
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        let transitionType: TransitionType
    
        init(transitionType: TransitionType) {
            self.transitionType = transitionType
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let inView   = transitionContext.containerView
            let toView   = transitionContext.view(forKey: .to)!
            let fromView = transitionContext.view(forKey: .from)!
    
            var frame = inView.bounds
    
            func flipTransform(angle: CGFloat, offset: CGFloat = 0) -> CATransform3D {
                var transform = CATransform3DMakeTranslation(offset, 0, 0)
                transform.m34 = -1.0 / 1600
                transform = CATransform3DRotate(transform, angle, 0, 1, 0)
                return transform
            }
    
            toView.frame = inView.bounds
            toView.alpha = 0
    
            let transformFromStart:  CATransform3D
            let transformFromEnd:    CATransform3D
            let transformFromMiddle: CATransform3D
            let transformToStart:    CATransform3D
            let transformToMiddle:   CATransform3D
            let transformToEnd:      CATransform3D
    
            switch transitionType {
            case .presenting:
                transformFromStart  = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: -.pi / 2)
                transformToStart    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: .pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
    
            case .dismissing:
                transformFromStart  = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: .pi / 2)
                transformToStart    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: -.pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
            }
    
            toView.layer.transform = transformToStart
            fromView.layer.transform = transformFromStart
            inView.addSubview(toView)
    
            UIView.animateKeyframes(withDuration: self.transitionDuration(using: transitionContext), delay: 0, options: [], animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.0) {
                    toView.alpha = 0
                    fromView.alpha = 1
                }
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                    toView.layer.transform = transformToMiddle
                    fromView.layer.transform = transformFromMiddle
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0) {
                    toView.alpha = 1
                    fromView.alpha = 0
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                    toView.layer.transform = transformToEnd
                    fromView.layer.transform = transformFromEnd
                }
            }, completion: { finished in
                toView.layer.transform = CATransform3DIdentity
                fromView.layer.transform = CATransform3DIdentity
                toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    动画控制器只需执行
    。transitionFlipFromRight
    是演示文稿,或者
    。transitionFlipFromLeft
    如果在Swift 3中取消:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        modalTransitionStyle = .flipHorizontal   // use `.FlipHorizontal` in Swift 2
    }
    
    class SecondViewController: UIViewController {
    
        let customTransitionDelegate = TransitioningDelegate()
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            modalPresentationStyle = .custom   // use `.Custom` in Swift 2
            transitioningDelegate = customTransitionDelegate
        }
    
        ...
    }
    
    class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .presenting)
        }
    
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .dismissing)
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        var animationTransitionOptions: UIViewAnimationOptions
    
        init(transitionType: TransitionType) {
            switch transitionType {
            case .presenting:
                animationTransitionOptions = .transitionFlipFromRight
            case .dismissing:
                animationTransitionOptions = .transitionFlipFromLeft
            }
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            //let inView   = transitionContext.containerView
            let toView   = transitionContext.viewController(forKey: .to)?.view
            let fromView = transitionContext.viewController(forKey: .from)?.view
    
            UIView.transition(from: fromView!, to: toView!, duration: transitionDuration(using: transitionContext), options: animationTransitionOptions.union(.curveEaseInOut)) { finished in
                transitionContext.completeTransition(true)
            }
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        let transitionType: TransitionType
    
        init(transitionType: TransitionType) {
            self.transitionType = transitionType
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let inView   = transitionContext.containerView
            let toView   = transitionContext.view(forKey: .to)!
            let fromView = transitionContext.view(forKey: .from)!
    
            var frame = inView.bounds
    
            func flipTransform(angle: CGFloat, offset: CGFloat = 0) -> CATransform3D {
                var transform = CATransform3DMakeTranslation(offset, 0, 0)
                transform.m34 = -1.0 / 1600
                transform = CATransform3DRotate(transform, angle, 0, 1, 0)
                return transform
            }
    
            toView.frame = inView.bounds
            toView.alpha = 0
    
            let transformFromStart:  CATransform3D
            let transformFromEnd:    CATransform3D
            let transformFromMiddle: CATransform3D
            let transformToStart:    CATransform3D
            let transformToMiddle:   CATransform3D
            let transformToEnd:      CATransform3D
    
            switch transitionType {
            case .presenting:
                transformFromStart  = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: -.pi / 2)
                transformToStart    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: .pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
    
            case .dismissing:
                transformFromStart  = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: .pi / 2)
                transformToStart    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: -.pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
            }
    
            toView.layer.transform = transformToStart
            fromView.layer.transform = transformFromStart
            inView.addSubview(toView)
    
            UIView.animateKeyframes(withDuration: self.transitionDuration(using: transitionContext), delay: 0, options: [], animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.0) {
                    toView.alpha = 0
                    fromView.alpha = 1
                }
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                    toView.layer.transform = transformToMiddle
                    fromView.layer.transform = transformFromMiddle
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0) {
                    toView.alpha = 1
                    fromView.alpha = 0
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                    toView.layer.transform = transformToEnd
                    fromView.layer.transform = transformFromEnd
                }
            }, completion: { finished in
                toView.layer.transform = CATransform3DIdentity
                fromView.layer.transform = CATransform3DIdentity
                toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    有关iOS 7中引入的自定义转换的更多信息,请参阅WWDC 2013视频

  • 应该承认,上面的
    AnimationController
    实际上过于简化,因为我们使用的是
    transform(from:to:…)
    。这会产生一个不可取消的动画(如果您使用的是交互式转换)。它还删除了“from”视图本身,从iOS 8开始,这实际上是演示控制器的工作

    因此,您确实希望使用
    UIView.animate
    API来制作翻转动画。我很抱歉,因为下面涉及到在关键帧动画中使用一些不直观的
    CATTransferM3D
    ,但它会导致翻转动画,然后可以进行可取消的交互转换

    因此,在Swift 3中:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        modalTransitionStyle = .flipHorizontal   // use `.FlipHorizontal` in Swift 2
    }
    
    class SecondViewController: UIViewController {
    
        let customTransitionDelegate = TransitioningDelegate()
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            modalPresentationStyle = .custom   // use `.Custom` in Swift 2
            transitioningDelegate = customTransitionDelegate
        }
    
        ...
    }
    
    class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .presenting)
        }
    
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AnimationController(transitionType: .dismissing)
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        var animationTransitionOptions: UIViewAnimationOptions
    
        init(transitionType: TransitionType) {
            switch transitionType {
            case .presenting:
                animationTransitionOptions = .transitionFlipFromRight
            case .dismissing:
                animationTransitionOptions = .transitionFlipFromLeft
            }
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            //let inView   = transitionContext.containerView
            let toView   = transitionContext.viewController(forKey: .to)?.view
            let fromView = transitionContext.viewController(forKey: .from)?.view
    
            UIView.transition(from: fromView!, to: toView!, duration: transitionDuration(using: transitionContext), options: animationTransitionOptions.union(.curveEaseInOut)) { finished in
                transitionContext.completeTransition(true)
            }
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
    class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
        enum TransitionType {
            case presenting
            case dismissing
        }
    
        let transitionType: TransitionType
    
        init(transitionType: TransitionType) {
            self.transitionType = transitionType
    
            super.init()
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            let inView   = transitionContext.containerView
            let toView   = transitionContext.view(forKey: .to)!
            let fromView = transitionContext.view(forKey: .from)!
    
            var frame = inView.bounds
    
            func flipTransform(angle: CGFloat, offset: CGFloat = 0) -> CATransform3D {
                var transform = CATransform3DMakeTranslation(offset, 0, 0)
                transform.m34 = -1.0 / 1600
                transform = CATransform3DRotate(transform, angle, 0, 1, 0)
                return transform
            }
    
            toView.frame = inView.bounds
            toView.alpha = 0
    
            let transformFromStart:  CATransform3D
            let transformFromEnd:    CATransform3D
            let transformFromMiddle: CATransform3D
            let transformToStart:    CATransform3D
            let transformToMiddle:   CATransform3D
            let transformToEnd:      CATransform3D
    
            switch transitionType {
            case .presenting:
                transformFromStart  = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: -.pi / 2)
                transformToStart    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: .pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
    
            case .dismissing:
                transformFromStart  = flipTransform(angle: 0,        offset: -inView.bounds.size.width / 2)
                transformFromEnd    = flipTransform(angle: .pi,      offset: -inView.bounds.size.width / 2)
                transformFromMiddle = flipTransform(angle: .pi / 2)
                transformToStart    = flipTransform(angle: -.pi,     offset: inView.bounds.size.width / 2)
                transformToMiddle   = flipTransform(angle: -.pi / 2)
                transformToEnd      = flipTransform(angle: 0,        offset: inView.bounds.size.width / 2)
    
                toView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
            }
    
            toView.layer.transform = transformToStart
            fromView.layer.transform = transformFromStart
            inView.addSubview(toView)
    
            UIView.animateKeyframes(withDuration: self.transitionDuration(using: transitionContext), delay: 0, options: [], animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.0) {
                    toView.alpha = 0
                    fromView.alpha = 1
                }
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                    toView.layer.transform = transformToMiddle
                    fromView.layer.transform = transformFromMiddle
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0) {
                    toView.alpha = 1
                    fromView.alpha = 0
                }
                UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                    toView.layer.transform = transformToEnd
                    fromView.layer.transform = transformFromEnd
                }
            }, completion: { finished in
                toView.layer.transform = CATransform3DIdentity
                fromView.layer.transform = CATransform3DIdentity
                toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
                fromView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
        }
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1.0
        }
    }
    
  • 仅供参考,iOS 8使用表示控制器扩展了自定义转换模型。有关更多信息,请参阅WWDC 2014视频

    无论如何,如果在转换结束时,“from”视图不再可见,您应该指示您的演示控制器将其从视图层次结构中删除,例如:

    class PresentationController: UIPresentationController {
        override var shouldRemovePresentersView: Bool { return true }
    }
    
    显然,您必须通知您的
    TransitioningDelegate
    此演示控制器:

    class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
    
        ...
    
        func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
            return PresentationController(presentedViewController: presented, presenting: presenting)
        }            
    }
    

  • 此答案已为Swift 3更新。如果您希望看到Swift 2的实施,请参阅。

    您能否澄清这一点?这是在自定义容器视图控制器的上下文中吗?@Rob第一个视图控制器有背景和按钮,第二个只有背景(我还不能回去)我只是想弄清楚使用故事板segue子类与标准演示(具有翻转过渡样式)之间是否有特殊的意图或者使用通过转换代理和动画控制器启用的iOS 7自定义转换模式。如果使用自定义segue执行此操作,则必须执行额外的工作以保持视图控制器层次结构与视图层次结构同步。现在最好使用表示转换、导航控制器或自定义容器控制器。我将在下面的回答中概述其中的第一个问题。非常感谢您提供的详细答案-我稍后会尝试这个问题,我相信我会给您“绿色记号”-但我有一个问题。为什么你通常使用这个而不是定制的序列?它要复杂得多,不是吗?你说这是因为视图控制器的层次结构,但它在旧版本的Xcode或iOS中工作得很好,不是吗?我建议查看WWDC 2011视频的前5-10分钟,其中讨论了保持视图控制器层次结构与视图同步的重要性等级制度(Custom Transitions(自定义转换)视频也谈到了这一点,但我认为2011年的视频更好地概括了基本问题,IMHO。)总之,您可以使自定义segue方法工作得很好,但由于视图控制器层次结构不同步,您可能无法将某些事件传递给它。最后一个问题是:“让vc=storyboard?.InstanceEviewControllerWithiIdentifier(“第二个”)作为UIViewController”这是我要去的视图控制器还是我要去的视图控制器?我如何给视图控制器一个标识符?我必须给它一个故事板ID吗?