[英]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.