iOS Swift:自定义互动过渡

时间:2019-02-17 12:18:09

标签: ios swift uiviewanimationtransition

我写了一个自定义的滑动过渡,在模式演示中效果很好。但是在推送演示中,“ to”视图位置没有动画。

我已经尝试过使用相同的代码切换alpha转换,并且可以正常工作。

from视图的工作原理完美,只是在动画过程中保持固定的view。

func transitionAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
    let duration = transitionDuration(using: transitionContext)
    let container = transitionContext.containerView

    let toController = transitionContext.viewController(forKey: .to)
    toController?.beginAppearanceTransition(true, animated: true)

    guard
    let to = transitionContext.view(forKey: .to),
    let from = transitionContext.view(forKey: .from)
        else {
        print("To or from view are nil!")
        fatalError()
    }

    container.addSubview(to)

    let animator = UIViewPropertyAnimator(duration: duration, curve: .linear)
    var toStartingPoint: CGPoint
    var fromEndingPoint: CGPoint

    switch self.from {
    case .down:
        toStartingPoint = CGPoint(x: 0, y: -from.bounds.height)
        fromEndingPoint = CGPoint(x: 0, y: from.bounds.height)
    case .top:
        toStartingPoint = CGPoint(x: 0, y: from.bounds.height)
        fromEndingPoint = CGPoint(x: 0, y: -from.bounds.height)
    case .right:
        toStartingPoint = CGPoint(x: from.bounds.width, y: 0)
        fromEndingPoint = CGPoint(x: -from.bounds.width, y: 0)
    case .left:
        toStartingPoint = CGPoint(x: -from.bounds.width, y: 0)
        fromEndingPoint = CGPoint(x: from.bounds.width, y: 0)
    }

    to.transform = CGAffineTransform(translationX: toStartingPoint.x, y: toStartingPoint.y)

    animator.addAnimations({
        from.transform = CGAffineTransform(translationX: fromEndingPoint.x, y: fromEndingPoint.y)
    }, delayFactor: 0.0)

    animator.addAnimations({
        to.transform = .identity
    }, delayFactor: 0.0)


    animator.addCompletion { [weak self] position in
        switch position {
        case .start:
            self?.auxCancelCompletion?()
            transitionContext.completeTransition(false)
            self?.auxAnimationsCancel?()
        case .end:
            self?.auxEndCompletion?()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            from.transform = .identity
            to.transform = .identity
        default:
            transitionContext.completeTransition(false)
            self?.auxAnimationsCancel?()
        }
    }

    if let auxAnimations = auxAnimations {
        animator.addAnimations(auxAnimations)
    }

    self.animator = animator
    self.context = transitionContext

    animator.addCompletion { [unowned self] _ in
        self.animator = nil
        self.context = nil
    }
    animator.isUserInteractionEnabled = true

    return animator
}

我当时想这是关于委托的问题,但是正确设置了navigationDelgate,否则我认为看不到任何动画。

代表设置:

override func viewDidLoad() {
    super.viewDidLoad()
    transitionHelper = SwipeInteractiveTransitionHelper(withDelegate: self)
}


extension TodayViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return transitionHelper?.swipeTransition
    }

    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return transitionHelper?.swipeTransition
    }
}

这是自定义推送协调器,其中viewController是下一个视图控制器,我将委托附加在这里。

    case .pushCustom:
        guard let navigationController = currentViewController.navigationController else {
            fatalError("Can't push a view controller without a current navigation controller")
        }
        guard let current = currentViewController as? UINavigationControllerDelegate else {
            fatalError("Can't push a view controller without a current  navigation delegate")
        }
        navigationController.delegate = current
        navigationController.pushViewController(viewController, animated: true) { [weak self] in
            self?.currentViewController = SceneCoordinator.actualViewController(for: viewController)
            completion?()
        }

2 个答案:

答案 0 :(得分:0)

我所做的就是这种方式。希望对您有帮助。

VC:UIViewController
{
@IBAction func test(_ sender: Any){
navigationController?.delegate = self
let destine = storyboard?.instantiateViewController(withIdentifier: "target")
navigationController?.pushViewController(destine!, animated: true)
}

  func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
    return Traistion()
}

}


 class Traistion:  NSObject, UIViewControllerAnimatedTransitioning{


func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    let animator  = transitionAnimator(using: transitionContext)
    animator.startAnimation()
}

var animator: UIViewPropertyAnimator!
var context: UIViewControllerContextTransitioning!

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval{
    return 1.0
}

enum Direction {
    case down
    case top
    case left
    case right
}

var from : Direction = .left

var auxCancelCompletion :(()->())? = {
    return nil
}()

var auxAnimationsCancel :(()->())? = {
    return nil
}()

var auxEndCompletion :(()->())? = {
    return nil
}()


var auxAnimations : (()->())?
func transitionAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
    let duration = transitionDuration(using: transitionContext)
    let container = transitionContext.containerView

    let toController = transitionContext.viewController(forKey: .to)
    toController?.beginAppearanceTransition(true, animated: true)

    guard
        let to = transitionContext.view(forKey: .to),
        let from = transitionContext.view(forKey: .from)
        else {
            print("To or from view are nil!")
            fatalError()
    }

    container.addSubview(to)

    let animator = UIViewPropertyAnimator(duration: duration, curve: .linear)
    var toStartingPoint: CGPoint
    var fromEndingPoint: CGPoint

    switch self.from {
    case .down:
        toStartingPoint = CGPoint(x: 0, y: -from.bounds.height)
        fromEndingPoint = CGPoint(x: 0, y: from.bounds.height)
    case .top:
        toStartingPoint = CGPoint(x: 0, y: from.bounds.height)
        fromEndingPoint = CGPoint(x: 0, y: -from.bounds.height)
    case .right:
        toStartingPoint = CGPoint(x: from.bounds.width, y: 0)
        fromEndingPoint = CGPoint(x: -from.bounds.width, y: 0)
    case .left:
        toStartingPoint = CGPoint(x: -from.bounds.width, y: 0)
        fromEndingPoint = CGPoint(x: from.bounds.width, y: 0)
    }

    to.transform = CGAffineTransform(translationX: toStartingPoint.x, y: toStartingPoint.y)

    animator.addAnimations({
        from.transform = CGAffineTransform(translationX: fromEndingPoint.x, y: fromEndingPoint.y)
    }, delayFactor: 0.0)

    animator.addAnimations({
        to.transform = .identity
    }, delayFactor: 0.0)


    animator.addCompletion { [weak self] position in
        switch position {
        case .start:
            self?.auxCancelCompletion?()
            transitionContext.completeTransition(false)
            self?.auxAnimationsCancel?()
        case .end:
            self?.auxEndCompletion?()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            from.transform = .identity
            to.transform = .identity
        default:
            transitionContext.completeTransition(false)
            self?.auxAnimationsCancel?()
        }
    }

    if let auxAnimations = auxAnimations {
        animator.addAnimations(auxAnimations)
    }

    self.animator = animator
    self.context = transitionContext

    animator.addCompletion { [unowned self] _ in
       //self.animator = nil
       // self.context = nil
    }
    animator.isUserInteractionEnabled = true

    return animator
   }

 }

使用interruptibleAnimator时。它将至少两次调用此函数,并且您应该提供相同的动画师,但要提供不同的动画师。因此,您必须这样称呼它:

  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

 animator =    transitionAnimator(using: transitionContext) as! UIViewPropertyAnimator
 animator.startAnimation()
}
func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
    return  animator
}

var animator: UIViewPropertyAnimator!
var context: UIViewControllerContextTransitioning!

或更简单的这样:

 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
}
func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
    if    animator == nil {animator =    transitionAnimator(using: transitionContext) as! UIViewPropertyAnimator
        animator.startAnimation()}
    return  animator
}

这里animator将被调用两次,并且是相同的。如果

 func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { 
   return transitionAnimator(using: transitionContext) } 

这不是正确的用法,因为动画师会有所不同。

https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1829434-interruptibleanimator

请参阅文档的最后一行。

答案 1 :(得分:0)

解决了为目标视图的快照制作动画,而不是直接为目标视图制作动画。

let to = transitionContext.view(forKey:.to) 让toViewSnapshot = to.snapshotView(afterScreenUpdates:true)

只需将toViewSnapshot用于动画