在UIPercentDrivenInteractiveTransition中缓慢平移会导致故障 [英] Slowly panning in UIPercentDrivenInteractiveTransition results in glitch
问题描述
在我的应用中,我使用由平移手势触发的UIPercentDrivenInteractiveTransition关闭了一个viewController.我希望将我的viewController平移时拖动到右侧.但是,当我慢慢平移时,我会出现一个小故障:viewController会从左向右快速跳跃一点.这是转换的代码:
In my app I'm dismissing a viewController using a UIPercentDrivenInteractiveTransition triggered by a pan gesture. I'm expecting my viewController to be dragged to the right as I'm panning it. However when I slowly pan I get a glitch: the viewController quickly jumps from left to right a bit. Here's the code for the transition:
class FilterHideTransition: UIPercentDrivenInteractiveTransition {
let viewController: FilterViewController
var enabled = false
private let panGesture = UIPanGestureRecognizer()
private let tapGesture = UITapGestureRecognizer()
init(viewController: FilterViewController) {
self.viewController = viewController
super.init()
panGesture.addTarget(self, action: #selector(didPan(with:)))
panGesture.cancelsTouchesInView = false
panGesture.delegate = self
tapGesture.addTarget(self, action: #selector(didTap(with:)))
tapGesture.cancelsTouchesInView = false
tapGesture.delegate = self
viewController.view.addGestureRecognizer(panGesture)
viewController.view.addGestureRecognizer(tapGesture)
}
}
//MARK: - Actions
private extension FilterHideTransition {
@objc func didPan(with recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: viewController.view)
let percentage = translation.x / viewController.view.frame.size.width
print(percentage)
switch recognizer.state {
case .began:
enabled = true
viewController.dismiss(animated: true, completion: nil)
break
case .changed:
update(percentage)
break
case .ended:
completionSpeed = 0.3
if percentage > 0.5 {
finish()
} else {
cancel()
}
enabled = false
break
case .cancelled:
cancel()
enabled = false
break
default:
cancel()
enabled = false
break
}
}
@objc func didTap(with recognizer: UITapGestureRecognizer) {
viewController.dismiss(animated: true, completion: nil)
}
func isTouch(touch: UITouch, in view: UIView) -> Bool {
let touchPoint = touch.location(in: view)
return view.hitTest(touchPoint, with: nil) != nil
}
}
extension FilterHideTransition: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if gestureRecognizer == tapGesture {
return !isTouch(touch: touch, in: viewController.panel)
} else if gestureRecognizer == panGesture {
return !isTouch(touch: touch, in: viewController.heightSlider) &&
!isTouch(touch: touch, in: viewController.widthSlider) &&
!isTouch(touch: touch, in: viewController.priceSlider)
} else {
return true
}
}
}
这是动画师的代码:
class FilterHideAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.25
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? OverlayTabBarController
else { return }
let startFrame = fromVC.view.frame
let endFrame = CGRect(x: startFrame.size.width, y: 0, width: startFrame.size.width, height: startFrame.size.height)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0.0,
options: .curveEaseIn,
animations: {
fromVC.view.frame = endFrame
toVC.overlay.alpha = 0
},
completion: {
_ in
if transitionContext.transitionWasCancelled {
transitionContext.completeTransition(false)
} else {
transitionContext.completeTransition(true)
}
})
}
}
我的问题:如何防止这种故障的发生?
My question: How can I prevent this glitch from happening?
推荐答案
我测试了您的最小工作示例,同样的问题再次出现.我无法使用 UIView.animate
API进行修复,但是如果您使用 UIViewPropertyAnimator
,则不会出现此问题-唯一的缺点是 UIViewPropertyAnimator
仅可用于iOS 10 +.
I tested your minimal working example and the same issue reappears. I wasn't able to fix it using UIView.animate
API, but the issue does not appear if you use UIViewPropertyAnimator
- only drawback is that UIViewPropertyAnimator
is available only from iOS 10+.
iOS 10+解决方案
首先重构 HideAnimator
以实现 interruptibleAnimator(using:)
以返回执行过渡动画器的 UIViewPropertyAnimator
对象(请注意,根据文档我们应该为正在进行的过渡返回相同的动画对象):
First refactor HideAnimator
to implement interruptibleAnimator(using:)
to return a UIViewPropertyAnimator
object that performs the transition animator (note that as per documentation we are supposed to return the same animator object for ongoing transition):
class HideAnimator: NSObject, UIViewControllerAnimatedTransitioning {
fileprivate var propertyAnimator: UIViewPropertyAnimator?
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.25
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// use animator to implement animateTransition
let animator = interruptibleAnimator(using: transitionContext)
animator.startAnimation()
}
func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
// as per documentation, we need to return existing animator
// for ongoing transition
if let propertyAnimator = propertyAnimator {
return propertyAnimator
}
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
else { fatalError() }
let startFrame = fromVC.view.frame
let endFrame = CGRect(x: startFrame.size.width, y: 0, width: startFrame.size.width, height: startFrame.size.height)
let animator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), timingParameters: UICubicTimingParameters(animationCurve: .easeInOut))
animator.addAnimations {
fromVC.view.frame = endFrame
}
animator.addCompletion { (_) in
if transitionContext.transitionWasCancelled {
transitionContext.completeTransition(false)
} else {
transitionContext.completeTransition(true)
}
// reset animator because the current transition ended
self.propertyAnimator = nil
}
self.propertyAnimator = animator
return animator
}
}
要使其正常工作的最后一件事,是在 didPan(with:)
中删除以下行:
One last thing to make it work, in didPan(with:)
remove following line:
completionSpeed = 0.3
这将使用默认速度( 1.0
,或者您可以显式设置它).使用 interruptibleAnimator(using:)
时,完成速度是根据动画师的 fractionComplete
自动计算的.
This will use the default speed (which is 1.0
, or you can set it explicitly). When using interruptibleAnimator(using:)
the completion speed is automatically calculated based on the fractionComplete
of the animator.
这篇关于在UIPercentDrivenInteractiveTransition中缓慢平移会导致故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!