ฉันมี UIView ซึ่งเลื่อนเข้ามาจาก Botton หลังจากที่คุณ 1) แตะหรือ 2) เลื่อนไปด้านบน ตอนนี้คุณสามารถปิดได้โดย 1) แตะอีกครั้ง หรือ 2) เลื่อนไปที่ด้านล่างสุด
ฉันต้องการป้องกันไม่ให้: ปิด มุมมองหลังจากแตะเพียงครั้งเดียว และอนุญาตให้เลื่อนลงเพื่อปิดเท่านั้น
นี่คือรหัสปัจจุบันของฉัน: (จาก: https://www.swiftkickmobile.com/building-better-app-animations-swift-uiviewpropertyanimator)
/// A pan gesture that enters into the `began` state on touch down instead of waiting for a touches moved event.
class InstantPanGestureRecognizer: UIPanGestureRecognizer {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if (self.state == UIGestureRecognizer.State.began) { return }
super.touchesBegan(touches, with: event)
self.state = UIGestureRecognizer.State.began
}
}
@objc private func popupViewPanned(recognizer: UIPanGestureRecognizer) {
print(recognizer.state.rawValue)
switch recognizer.state {
case .began:
// Start the animations
animateTransitionIfNeeded(to: currentState.opposite, duration: 0.5)
// Pause all animations, since the next event may be a pan changed
runningAnimators.forEach { $0.pauseAnimation() }
// Keep track of each animator's progress
animationProgress = runningAnimators.map { $0.fractionComplete }
case .changed:
// Variable setup
let translation = recognizer.translation(in: popupView)
if UIDevice().userInterfaceIdiom == .phone {
switch UIScreen.main.nativeBounds.height {
case 1334, 1920, 2208:
var fraction = -translation.y / popupOffset
// adjust the fraction for the current state and reversed state
if currentState == .open { fraction *= -1 }
if runningAnimators[0].isReversed { fraction *= -1 }
// apply the new fraction
for (index, animator) in runningAnimators.enumerated() {
animator.fractionComplete = fraction + animationProgress[index]
}
case 2436, 2688, 1792:
var fraction = -translation.y / popupOffsetNotch
// adjust the fraction for the current state and reversed state
if currentState == .open { fraction *= -1 }
if runningAnimators[0].isReversed { fraction *= -1 }
// apply the new fraction
for (index, animator) in runningAnimators.enumerated() {
animator.fractionComplete = fraction + animationProgress[index]
}
default:
var fraction = -translation.y / popupOffsetNotch
// adjust the fraction for the current state and reversed state
if currentState == .open { fraction *= -1 }
if runningAnimators[0].isReversed { fraction *= -1 }
// apply the new fraction
for (index, animator) in runningAnimators.enumerated() {
animator.fractionComplete = fraction + animationProgress[index]
}
}
}
case .ended:
// variable setup
let yVelocity = recognizer.velocity(in: popupView).y
let shouldClose = yVelocity > 0
// if there is no motion, continue all animations and exit early
if yVelocity == 0 {
runningAnimators.forEach { $0.continueAnimation(withTimingParameters: nil, durationFactor: 0) }
break
}
// reverse the animations based on their current state and pan motion
switch currentState {
case .open:
if !shouldClose && !runningAnimators[0].isReversed { runningAnimators.forEach { $0.isReversed = !$0.isReversed } }
if shouldClose && runningAnimators[0].isReversed { runningAnimators.forEach { $0.isReversed = !$0.isReversed } }
case .closed:
if shouldClose && !runningAnimators[0].isReversed { runningAnimators.forEach { $0.isReversed = !$0.isReversed } }
if !shouldClose && runningAnimators[0].isReversed { runningAnimators.forEach { $0.isReversed = !$0.isReversed } }
}
// continue all animations
runningAnimators.forEach { $0.continueAnimation(withTimingParameters: nil, durationFactor: 0) }
default:
()
}
}
/// Animates the transition, if the animation is not already running.
private func animateTransitionIfNeeded(to state: State, duration: TimeInterval) {
// ensure that the animators array is empty (which implies new animations need to be created)
guard runningAnimators.isEmpty else { return }
// an animator for the transition
let transitionAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 1, animations: {
switch state {
case .open:
self.bottomConstraint.constant = 0
self.popupView.layer.cornerRadius = 10
case .closed:
if UIDevice().userInterfaceIdiom == .phone {
switch UIScreen.main.nativeBounds.height {
case 1334, 1920, 2208:
self.bottomConstraint.constant = self.popupOffset
case 2436, 2688, 1792:
self.bottomConstraint.constant = self.popupOffsetNotch
default:
self.bottomConstraint.constant = self.popupOffsetNotch
}
}
self.popupView.layer.cornerRadius = 5
}
self.view.layoutIfNeeded()
})
// the transition completion block
transitionAnimator.addCompletion { position in
// update the state
switch position {
case .start:
self.currentState = state.opposite
case .end:
self.currentState = state
case .current:
()
@unknown default:
return
}
// manually reset the constraint positions
switch self.currentState {
case .open:
self.bottomConstraint.constant = 0
case .closed:
if UIDevice().userInterfaceIdiom == .phone {
switch UIScreen.main.nativeBounds.height {
case 1334, 1920, 2208:
self.bottomConstraint.constant = self.popupOffset
case 2436, 2688, 1792:
self.bottomConstraint.constant = self.popupOffsetNotch
default:
self.bottomConstraint.constant = self.popupOffsetNotch
}
}
}
// remove all running animators
self.runningAnimators.removeAll()
}
// start all animators
transitionAnimator.startAnimation()
// keep track of all running animators
runningAnimators.append(transitionAnimator)
}