当 UIPercentDrivenInteractiveTransition 结束一半时在 Cancel 上触发动画
Trigger animation on Cancel when UIPercentDrivenInteractiveTransition ends half
如果交互结束并且动画被取消,强制自定义过渡返回到视图控制器的原始位置是否是一个好习惯。我有以下实现决定何时关闭平移手势上的视图控制器。如果平移手势提前结束,我希望在考虑持续时间与平移手势
上的progress
值成正比之前,动画回到原始位置,就像它的呈现方式一样
protocol AnimationControllerDelegate: AnyObject {
func shouldHandlePanelInteractionGesture() -> Bool
}
typealias PanGestureHandler = AnimationControllerDelegate & UIViewController & Animatable
final class CustomInteractionController: UIPercentDrivenInteractiveTransition, UIGestureRecognizerDelegate {
var interactionInProgress: Bool = false
private var shouldCompleteTransition: Bool = false
private var startTransitionY: CGFloat = 0
private var panGestureRecognizer: UIPanGestureRecognizer?
private weak var viewController: PanGestureHandler?
func wireToViewController(viewController: Any) {
guard let viewControllerDelegate = viewController as? PanGestureHandler else {
return
}
self.viewController = viewControllerDelegate
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestureRecognizer(_:)))
panGestureRecognizer = panGesture
panGestureRecognizer?.delegate = self
self.viewController?.view.addGestureRecognizer(panGesture)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith
otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc
func handlePanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer) {
guard let childView = gestureRecognizer.view,
let parentView = childView.superview,
let panGestureHandler = viewController else {
return
}
switch gestureRecognizer.state {
case .began:
break
case .changed:
let translation = gestureRecognizer.translation(in: parentView)
let velocity = gestureRecognizer.velocity(in: parentView)
let state = gestureRecognizer.state
if !panGestureHandler.shouldHandlePanelInteractionGesture() && percentComplete == 0 {
return
}
let verticalMovement = translation.y / childView.bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
let alphaValue = (1 - progress) * 0.4
panGestureHandler.shadowView.backgroundColor = Safety.Colors.backgroundViewColor(for: alphaValue)
if abs(velocity.x) > abs(velocity.y) && state == .began {
return
}
if !interactionInProgress {
interactionInProgress = true
startTransitionY = translation.y
viewController?.dismiss(animated: true, completion: nil)
} else {
shouldCompleteTransition = progress > 0.3
update(progress)
}
case .cancelled:
interactionInProgress = false
startTransitionY = 0
cancel()
case .ended:
interactionInProgress = false
startTransitionY = 0
if !shouldCompleteTransition {
// Can I call a custom transition here back to original position?
cancel()
} else {
finish()
}
case .failed:
interactionInProgress = false
startTransitionY = 0
cancel()
default:
break
}
}
}
Is it a good practice to force custom transition back to original position for a view controller if the interaction ends and animation is cancelled.
是的,我认为如果用户取消他们的手势,最好将其反转。但你不必“强迫”它。您完成转换,只需表明您是希望它完成还是反转。因此,如果您取消动画,它会自动反转并返回到它自动为您显示的位置。除了 cancel
.
,您无需执行任何操作
当然,这假定在动画师的动画完成块中,您在 completeTransition
中指出它是否完成:
UIView.animate(withDuration: 0.25, animations: {
...
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
因此,如果没有取消,动画将完成。但如果取消它会反转。
就个人而言,在我的手势识别器中,我通常会执行以下操作:
如果state
的手势是.began
,我会:
- 实例化
UIPercentDrivenInteractiveTransition
,UIViewControllerTransitioningDelegate
将 return 来自 interactionControllerForPresentation(using)
;和
- 执行
present
/dismiss
UIPercentDrivenInteractiveTransition
当手势更新为.changed
的state
时,只是update
UIPercentDrivenInteractiveTransition
;和
完成后,您可以 finish
或 cancel
它。而cancel
会自动触发反转动画
例如,我从左到右的“关闭”手势最终看起来像:
@objc func handleLeftToRightPan(_ gesture: UIPanGestureRecognizer) {
let percent = (gesture.translation(in: gesture.view).x) / gesture.view!.bounds.width
switch gesture.state {
case .began:
interactionController = UIPercentDrivenInteractiveTransition()
dismiss(animated: true)
interactionController?.update(percent)
case .changed:
interactionController?.update(percent)
case .ended, .cancelled:
let velocity = gesture.velocity(in: gesture.view).x
if velocity > 0 || (velocity == 0 && percent > 0.5) {
interactionController?.finish()
} else {
interactionController?.cancel()
}
interactionController = nil
default:
break
}
}
就我个人而言,对于 finish
与 cancel
逻辑,我检查是否:
是手势方向的velocity
(在从左到右的“解雇”手势中,这意味着我检查它是否是积极的x
velocity) ...这意味着如果用户轻弹,即使是屏幕的一小部分,我仍然会认为这是一个完全过渡的意图;
只有当 velocity
为零时,我才会检查完成百分比(例如,如果他们走了 ¾,停下来,然后放手,我假设他们想要 finish
手势,但如果他们只走了 ¼ 路然后停下来放手,我假设他们打算取消手势。
不用说,如果他们反转手势的方向,我认为取消了他们完成转换的意图
但是如您所见,我除了 cancel
什么都没做,动画自动反转。所以,在这里我从第二个视图控制器开始解雇过渡,回到第一个,但是 cancel
UIPercentDrivenInteractiveTransition
(在这种情况下,通过反转手势的方向并放手)和我的动画师将在其完成处理程序中将适当的 Bool
值传递给其完成处理程序中的 completeTransition
,它会自动返回动画,而我无需做任何工作:
如果交互结束并且动画被取消,强制自定义过渡返回到视图控制器的原始位置是否是一个好习惯。我有以下实现决定何时关闭平移手势上的视图控制器。如果平移手势提前结束,我希望在考虑持续时间与平移手势
上的progress
值成正比之前,动画回到原始位置,就像它的呈现方式一样
protocol AnimationControllerDelegate: AnyObject {
func shouldHandlePanelInteractionGesture() -> Bool
}
typealias PanGestureHandler = AnimationControllerDelegate & UIViewController & Animatable
final class CustomInteractionController: UIPercentDrivenInteractiveTransition, UIGestureRecognizerDelegate {
var interactionInProgress: Bool = false
private var shouldCompleteTransition: Bool = false
private var startTransitionY: CGFloat = 0
private var panGestureRecognizer: UIPanGestureRecognizer?
private weak var viewController: PanGestureHandler?
func wireToViewController(viewController: Any) {
guard let viewControllerDelegate = viewController as? PanGestureHandler else {
return
}
self.viewController = viewControllerDelegate
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestureRecognizer(_:)))
panGestureRecognizer = panGesture
panGestureRecognizer?.delegate = self
self.viewController?.view.addGestureRecognizer(panGesture)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith
otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc
func handlePanGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer) {
guard let childView = gestureRecognizer.view,
let parentView = childView.superview,
let panGestureHandler = viewController else {
return
}
switch gestureRecognizer.state {
case .began:
break
case .changed:
let translation = gestureRecognizer.translation(in: parentView)
let velocity = gestureRecognizer.velocity(in: parentView)
let state = gestureRecognizer.state
if !panGestureHandler.shouldHandlePanelInteractionGesture() && percentComplete == 0 {
return
}
let verticalMovement = translation.y / childView.bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
let alphaValue = (1 - progress) * 0.4
panGestureHandler.shadowView.backgroundColor = Safety.Colors.backgroundViewColor(for: alphaValue)
if abs(velocity.x) > abs(velocity.y) && state == .began {
return
}
if !interactionInProgress {
interactionInProgress = true
startTransitionY = translation.y
viewController?.dismiss(animated: true, completion: nil)
} else {
shouldCompleteTransition = progress > 0.3
update(progress)
}
case .cancelled:
interactionInProgress = false
startTransitionY = 0
cancel()
case .ended:
interactionInProgress = false
startTransitionY = 0
if !shouldCompleteTransition {
// Can I call a custom transition here back to original position?
cancel()
} else {
finish()
}
case .failed:
interactionInProgress = false
startTransitionY = 0
cancel()
default:
break
}
}
}
Is it a good practice to force custom transition back to original position for a view controller if the interaction ends and animation is cancelled.
是的,我认为如果用户取消他们的手势,最好将其反转。但你不必“强迫”它。您完成转换,只需表明您是希望它完成还是反转。因此,如果您取消动画,它会自动反转并返回到它自动为您显示的位置。除了 cancel
.
当然,这假定在动画师的动画完成块中,您在 completeTransition
中指出它是否完成:
UIView.animate(withDuration: 0.25, animations: {
...
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
因此,如果没有取消,动画将完成。但如果取消它会反转。
就个人而言,在我的手势识别器中,我通常会执行以下操作:
如果
state
的手势是.began
,我会:- 实例化
UIPercentDrivenInteractiveTransition
,UIViewControllerTransitioningDelegate
将 return 来自interactionControllerForPresentation(using)
;和 - 执行
present
/dismiss
UIPercentDrivenInteractiveTransition
- 实例化
当手势更新为
.changed
的state
时,只是update
UIPercentDrivenInteractiveTransition
;和完成后,您可以
finish
或cancel
它。而cancel
会自动触发反转动画
例如,我从左到右的“关闭”手势最终看起来像:
@objc func handleLeftToRightPan(_ gesture: UIPanGestureRecognizer) {
let percent = (gesture.translation(in: gesture.view).x) / gesture.view!.bounds.width
switch gesture.state {
case .began:
interactionController = UIPercentDrivenInteractiveTransition()
dismiss(animated: true)
interactionController?.update(percent)
case .changed:
interactionController?.update(percent)
case .ended, .cancelled:
let velocity = gesture.velocity(in: gesture.view).x
if velocity > 0 || (velocity == 0 && percent > 0.5) {
interactionController?.finish()
} else {
interactionController?.cancel()
}
interactionController = nil
default:
break
}
}
就我个人而言,对于 finish
与 cancel
逻辑,我检查是否:
是手势方向的
velocity
(在从左到右的“解雇”手势中,这意味着我检查它是否是积极的x
velocity) ...这意味着如果用户轻弹,即使是屏幕的一小部分,我仍然会认为这是一个完全过渡的意图;只有当
velocity
为零时,我才会检查完成百分比(例如,如果他们走了 ¾,停下来,然后放手,我假设他们想要finish
手势,但如果他们只走了 ¼ 路然后停下来放手,我假设他们打算取消手势。不用说,如果他们反转手势的方向,我认为取消了他们完成转换的意图
但是如您所见,我除了 cancel
什么都没做,动画自动反转。所以,在这里我从第二个视图控制器开始解雇过渡,回到第一个,但是 cancel
UIPercentDrivenInteractiveTransition
(在这种情况下,通过反转手势的方向并放手)和我的动画师将在其完成处理程序中将适当的 Bool
值传递给其完成处理程序中的 completeTransition
,它会自动返回动画,而我无需做任何工作: