繁体   English   中英

在 SwiftUI 中平滑加速旋转

[英]Smoothly speed up rotation in SwiftUI

我想在 SwiftUI 应用程序中平滑地加快形状的旋转速度,然后再次将其减慢到固定速度。 首先,我尝试使用@State Bool切换动画速度,就像我使用任何其他属性一样(例如.speed(speedUp ? 5.0 : 1.0) ),但我认为动画属性本身不是可动画的。 我也试过使用AnimatableModifier没有效果:

import SwiftUI

struct SpeedModifier: AnimatableModifier {
    var speed: Double
    var animatableData: Double {
        get { speed }
        set { speed = newValue }
    }

    func body(content: Content) -> some View {
        return content.animation(
                Animation
                    .linear(duration: 5.0)
                    .speed(speed)
                    .repeatForever(autoreverses: false)
            )
    }
}

struct SwiftUIView: View {
    @State var isRotating = false
    @State var speedUp = false
    var body: some View {
        Rectangle()
            .frame(width: 200, height: 200)
            .rotationEffect(.degrees(isRotating ? 360 : 0))
            .modifier(SpeedModifier(speed: speedUp ? 5.0 : 1.0))
            .onAppear {
                self.isRotating.toggle()
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                    self.speedUp.toggle()
                }
            }
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIView()
    }
}

你可以试试.timingCurve动画。 在这个例子中,矩形一直旋转得越来越慢:

struct RotatingView: View {

    @State private var rotationDegree = 0.0
    private var timeCurveAnimation: Animation {
        return Animation.timingCurve(0.5, 0.8, 0.8, 0.3, duration: 6)
            .repeatForever(autoreverses: false)
    }

    var body: some View {
        Rectangle()
            .frame(width: 200, height: 200)
            .rotationEffect(.degrees(rotationDegree))
            .onAppear() {
                withAnimation(self.timeCurveAnimation) {
                    self.rotationDegree = 720.0
                }
        }
    }

}

不幸的是,几乎没有文档 ( c0x:c0y:c1x: c1y: ) 参数是什么意思,从这个 github尝试了一些示例。 这篇文章描述更复杂的动画,应该很有用

据我所知,在撰写本文时(iOS 13,OSX 10.15),能够动态更改正在运行的动画是 Core Animation 的一项工作,它相当简单。

例如,使用 Core Animation,我们可以将以下动画添加到图层,使其每 0.5 秒无限期地旋转一次。

private let animation: CABasicAnimation = {
        
    let animation = CABasicAnimation(keyPath: "transform.rotation")
        
    animation.duration    = 0.5
    animation.fromValue   = 0.0
    animation.toValue     = 2.0 * -Float.pi
    animation.repeatCount = .infinity
        
    return animation
}()

完成后,以下扩展允许平滑更改图层的速度,并在原位停止图层。

import QuartzCore

extension CALayer {
    
    // Set up our view of the world such that time began from here,
    // so that we don't feel the need to change anything when our
    // properties are mutated. Handy for things like changing the
    // speed without first snapping back to the model state.
    
    func syncTimeToCurrent() {
        timeOffset = convertTime(CACurrentMediaTime(), from: nil)
        beginTime  = CACurrentMediaTime()
    }

    // Attempt to sync up the model transform with the presentation
    // transform. Handy for preventing the presentation from snapping
    // back to the model after removing a transform animation.
    
    func syncTransformToPresentation() {
        if let presentationTransform = presentation()?.transform {
            transform = presentationTransform
        }
    }
}

你会像这样使用它。 省略了如何实例化视图的细节; 在我的例子中,视图是一个包含 3 个图像子层的层托管视图,其中两个是静态的,一个是旋转的。

final class Rotating: NSView {

    private let animation: CABasicAnimation = {
        
        let animation = CABasicAnimation(keyPath: "transform.rotation")
        
        animation.duration    = 0.5
        animation.fromValue   = 0.0
        animation.toValue     = 2.0 * -Float.pi
        animation.repeatCount = .infinity
        
        return animation
    }()
    
    private let rotatingLayer: CALayer = { return CALayer() }()

    // Our speed is that of our rotating layer; return it to callers when
    // requested, and allow them to set it.

    var speed: Float {
        get { return rotatingLayer.speed }
        set {
            
            // Starting rotation from a dead stop is just adding
            // the animation to the layer.
            
            func run() {
                rotatingLayer.add(animation, forKey: nil)
            }
            
            // Key to setting the speed, if we are already rotating,
            // is to ensure we don't jump to the start position when
            // we do that, so set up the layer's view of time such
            // that it'll feel there's nothing to do in that regard.
            
            func set() {
                rotatingLayer.syncTimeToCurrent()
            }
            
            // Stopping rotation is just removing the transform
            // animation, but we ideally want to halt it where it is
            // at the moment, rather than having it snap back to the
            // original position.
            
            func off() {
                rotatingLayer.syncTransformToPresentation()
                rotatingLayer.removeAllAnimations()
            }
            
            // If we're being asked to set a zero speed, then it's
            // likely that the caller knows things that we don't,
            // such as that we're about to disappear. Stop rotation,
            // so things are in a well-defined state.
            //
            // If the new speed isn't zero, but our current speed is
            // zero, then we need to run.
            //
            // Otherwise, we need to set the already-running rotation
            // to the new speed.
            
            if      newValue == .zero { off() }
            else if speed    == .zero { run() }
            else                      { set() }
            
            rotatingLayer.speed = newValue
        }
    }
}

所以,那里相当简单,真的; 所有的工具都可以以一种直接的方式动态地修改东西,所以你可以做同样的事情,然后将视图导入 SwiftUI,绑定它等等。

我很想有人解释如何在纯 SwiftUI 中完成同样的事情,而不必进入 Core Animation。 目前,我就是这样做的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM