У меня есть 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)
}