4

我正在演示一个小型自定义视图控制器(它是一个菜单),如果您改变主意,我希望您能够在中途停止演示。

根据这个 WWDC 2016 视频,这样做应该很容易,只需要实现interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating并返回,比如UIViewPropertyAnimator.

但是,我认为情况并非如此。如果我在那里返回我的属性动画师,并以编程方式调用我的视图控制器在过渡的中途被解除,它会等到动画完全结束以发生解除动画。它不会中断。

*注意:这是使用“普通”动画控制器,而不是交互式动画控制器。根据视频,这应该没问题,它本质上不是交互式过渡”

我设置了相应的控制器,如图所示:

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return MyMenuAnimationController(type: .presentation)
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return MyMenuAnimationController(type: .dismissal)
}

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    return MyMenuPresentationController(presentedViewController: presented, presenting: presenting)
}
class MyMenuAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    enum AnimationControllerType { case presentation, dismissal }
    
    let type: AnimationControllerType
    
    var animatorForCurrentSession: UIViewPropertyAnimator?
    
    init(type: AnimationControllerType) {
        self.type = type
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 5.0 // Artificially long for testing
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if type == .presentation, let myMenuMenu: MyMenuMenu = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? MyMenuMenu {
            transitionContext.containerView.addSubview(myMenuMenu.view)
        }
        
        interruptibleAnimator(using: transitionContext).startAnimation()
    }
        
    func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
        if let animatorForCurrentSession = animatorForCurrentSession {
            return animatorForCurrentSession
        }
        
        let propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), dampingRatio: 0.75)
        propertyAnimator.isInterruptible = true
        propertyAnimator.isUserInteractionEnabled = true
        
        let isPresenting = type == .presentation
        
        guard let myMenuMenu: MyMenuMenu = {
            return (isPresenting ? transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) : transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)) as? MyMenuMenu
        }() else {
            preconditionFailure("Menu should be accessible")
        }
        
        myMenuMenu.view.frame = transitionContext.finalFrame(for: myMenuMenu)
        
        let initialAlpha: CGFloat = isPresenting ? 0.0 : 1.0
        let finalAlpha: CGFloat = isPresenting ? 1.0 : 0.0
        
        myMenuMenu.view.alpha = initialAlpha
        
        propertyAnimator.addAnimations {
            myMenuMenu.view.alpha = finalAlpha
        }
        
        propertyAnimator.addCompletion { (position) in
            guard position == .end else { return }
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            self.animatorForCurrentSession = nil
        }
        
        self.animatorForCurrentSession = propertyAnimator
        return propertyAnimator
    }
    
    private func calculateTranslationRequired(forMyMenuMenuFrame myMenuMenuFrame: CGRect, toDesiredPoint desiredPoint: CGPoint) -> CGVector {
        let centerPointOfMenuView = CGPoint(x: myMenuMenuFrame.origin.x + (myMenuMenuFrame.width / 2.0), y: myMenuMenuFrame.origin.y + (myMenuMenuFrame.height / 2.0))
        let translationRequired = CGVector(dx: desiredPoint.x - centerPointOfMenuView.x, dy: desiredPoint.y - centerPointOfMenuView.y)
        return translationRequired
    }
}

还有一个简单的演示控制器:

class MyMenuPresentationController: UIPresentationController {
    override var frameOfPresentedViewInContainerView: CGRect {
        return CGRect(x: 100, y: 100, width: 100, height: 100)
    }
}

但正如我所说,如果我简单地呈现它,然后在 5 秒动画的 1 秒内将其关闭(表示有人在视图控制器之外点击以将其关闭中间过渡),则在动画完成之前不会发生任何事情。就好像它已排队,但在演示完成之前不会执行。

let myMenuMenu = MyMenuMenu()
present(myMenuMenu, animated: true, completion: nil)

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
    myMenuMenu.dismiss(animated: true, completion: nil)
}

我是否误解了自定义视图控制器转换流程的核心部分?我必须使它成为交互式过渡吗?还有什么?如果需要交互式转换,我应该在哪里更新我的代码中的交互控制器的进度?使用手势很明显,但这只是“将 0 alpha 淡化为 1 alpha”的动画。

4

0 回答 0