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

如何解决如何使用 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.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?