我想在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()
}
}
答案 0 :(得分:1)
您可以尝试制作const siteBucket = new s3.Bucket(this, 'SiteBucket', {
bucketName: siteDomain,
websiteIndexDocument: 'index.html',
websiteErrorDocument: 'error.html',
publicReadAccess: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
new cdk.CfnOutput(this, 'Bucket', {value: siteBucket.bucketName});
const wwwRedirectBucket = new s3.Bucket(this, 'WwwBucket', {
bucketName: wwwDomain,
websiteRedirect: {hostName: siteDomain, protocol: RedirectProtocol.HTTPS},
publicReadAccess: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const certificateArn = "arn:aws:acm:etcetc"
const distribution = new cloudfront.CloudFrontWebDistribution(this, 'SiteDistribution', {
aliasConfiguration: {
acmCertRef: certificateArn,
names: [siteDomain],
sslMethod: cloudfront.SSLMethod.SNI,
securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_1_2016,
},
originConfigs: [
{
s3OriginSource: {
s3BucketSource: siteBucket
},
behaviors: [{isDefaultBehavior: true}],
}
]
});
const wwwDistribution = new cloudfront.CloudFrontWebDistribution(this, 'WwwDistribution', {
aliasConfiguration: {
acmCertRef: certificateArn,
names: [wwwDomain],
sslMethod: cloudfront.SSLMethod.SNI,
securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_1_2016,
},
originConfigs: [
{
s3OriginSource: {
s3BucketSource: wwwRedirectBucket
},
behaviors: [{isDefaultBehavior: true}],
}
]
});
动画。在此示例中,矩形一直旋转得越来越慢:
.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
}
}
}
}
参数的含义,尝试了this github中的一些示例。
in this article描述了更复杂的动画,它应该很有用
答案 1 :(得分:1)
就我所能确定的,截至撰写本文时(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。此刻,我就是这样做的。