我正在演示一个小型自定义视图控制器(它是一个菜单),如果您改变主意,我希望您能够在中途停止演示。
根据这个 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”的动画。