如何使用 UIViewPropertyAnimator 实现三模式转换?

问题描述

我尝试使用 UIViewPropertyAnimator 实现三种模式转换,我能够成功实现两种模式转换,但无法执行中间模式向下动画。以下 GIF 是目前实现的效果

enter image description here

func setPanGesture() -> InstantPanGestureRecognizer  {
    let recognizer = InstantPanGestureRecognizer()
    recognizer.addTarget(self,action: #selector(popupViewPanned(recognizer:)))
    return recognizer
}

func intialSetUp() {
    
    topSmallView.alpha  = 0.0
    topCalendarView.alpha  = 0.0
    popViewTopConstraint.constant = self.popviewIntialTopConstraint()

    tapAndSwipeButton.addGestureRecognizer(setPanGesture())
    topCalendarView.addGestureRecognizer(setPanGesture())
    topSmallView.addGestureRecognizer(setPanGesture())

}

@objc private func popupViewPanned(recognizer: UIPanGestureRecognizer) {
    
    let veLocity = recognizer.veLocity(in: self.view.superview)
    print("VeLocity \(veLocity)")

    switch recognizer.state {
    
    case .began:
        panDidBegan(recognizer: recognizer)
    case .changed:
        panDidChanged(recognizer: recognizer)
    case .ended:
        panDidEnd(recognizer: recognizer)
    default:
        ()
    }
}

//MARK:- Panning Helper

func panDidBegan(recognizer : UIPanGestureRecognizer) {
    
    var animationState : AnimationState =  currentState.opposite
    
    if currentState == .secondary {
        let veLocity = recognizer.veLocity(in: self.view.superview)
        let isverticalGesture = abs(veLocity.y) > abs(veLocity.x)
        
        if (isverticalGesture) {
            if (veLocity.y > 0) {
                animationState = .primary
            } else {
                animationState = .closed
            }
        }
    }else if currentState == .primary {
        let veLocity = recognizer.veLocity(in: self.view.superview)
        let isverticalGesture = abs(veLocity.y) > abs(veLocity.x)
        
        if (isverticalGesture) {
            if (veLocity.y > 0) {
                animationState = .primary
            } else {
                animationState = .secondary
            }
        }
    }

    animateTransitionIfNeeded(to: animationState,duration: 1)
    
    // 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 }
}

func panDidChanged(recognizer : UIPanGestureRecognizer) {
    
    var translationView : UIView
    
    if currentState == .primary {
        translationView = topSmallView
    }else if currentState == .secondary {
        translationView = topCalendarView
    }else{
        translationView = view
    }
    
    let translation = recognizer.translation(in: translationView)
    var fraction = -translation.y / translationView.frame.size.height
    print("fraction\(fraction)")

    // adjust the fraction for the current state and reversed state
    if currentState == .primary || currentState == .secondary {
        fraction *= -1
    }
    
    if  runningAnimators.count > 0 {
        if  runningAnimators[0].isReversed { fraction *= -1 }
    }
    
    // apply the new fraction
    for (index,animator) in runningAnimators.enumerated() {
        let fractionCom = (fraction + animationProgress[index])
        print("fractionCom\(fractionCom)")
        animator.fractionComplete = fractionCom
    }
}

func panDidEnd(recognizer : UIPanGestureRecognizer) {
    
    var translationView : UIView

    if currentState == .primary {
        translationView = topSmallView
    }else if currentState == .secondary {
        translationView = topCalendarView
    }else{
        translationView = view
    }
    
    // variable setup
    let yVeLocity = recognizer.veLocity(in: translationView).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) }
        return
    }
    
    // reverse the animations based on their current state and pan motion
    if  runningAnimators.count > 0 {
        
        switch currentState {
        case .primary:
            if !shouldClose && !runningAnimators[0].isReversed { runningAnimators.forEach { $0.isReversed = !$0.isReversed } }
            if shouldClose && runningAnimators[0].isReversed { runningAnimators.forEach { $0.isReversed = !$0.isReversed } }
        case .secondary:
            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) }
}

//MARK:- View Animator Helper

/// Animates the transition,if the animation is not already running.
private func animateTransitionIfNeeded(to state: AnimationState,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.0,animations: {
        switch state {
        case .primary:
            self.topBaseViewTopConstraint.constant = self.fullOpenStateBaseViewYosition()
            self.topCalendarView.alpha = 0
            self.topSmallView.alpha = 1
            break
        case .secondary:
            self.popViewTopConstraint.constant = 30.0
            self.swipeButtonBottomConstraint.constant = 0.0
            self.topBaseViewTopConstraint.constant = 0.0
            self.topCalendarView.alpha = 1
            self.topSmallView.alpha = 0
            break
        case .closed:
            self.popViewTopConstraint.constant = self.popviewIntialTopConstraint()
            self.swipeButtonBottomConstraint.constant = 60.0
            self.topCalendarView.alpha = 0
            self.topSmallView.alpha = 0
            break
        }
        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:
            break
        }
        
        // manually reset the constraint positions
        switch self.currentState {
        case .primary:
            self.topBaseViewTopConstraint.constant = self.fullOpenStateBaseViewYosition()
            self.topCalendarView.alpha = 0
            self.topSmallView.alpha = 1

        case .secondary:
            self.popViewTopConstraint.constant = 30.0
            self.topBaseViewTopConstraint.constant = 0.0
            self.swipeButtonBottomConstraint.constant = 0.0
            self.topCalendarView.alpha = 1
            self.topSmallView.alpha = 0
        case .closed:
            self.popViewTopConstraint.constant = self.popviewIntialTopConstraint()
            self.swipeButtonBottomConstraint.constant = 60.0
            self.topCalendarView.alpha = 0
            self.topSmallView.alpha = 0
        }
        
        // remove all running animators
        self.runningAnimators.removeAll()
        
    }

    transitionAnimator.startAnimation()
    runningAnimators.append(transitionAnimator)
    
}

如何使用 UIViewPropertyAnimator 实现这个动画?。非常感谢 谢谢。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...