【问题标题】:Problem animating with animatableData in SwiftUI在 SwiftUI 中使用 animatableData 制作动画的问题
【发布时间】:2020-03-29 02:06:36
【问题描述】:

SwiftUI 不仅为我们提供了自动动画……它还允许我们使用 animatableData 属性控制动画的发生方式。太酷了!

只有我不能让它工作。以下代码在屏幕上显示一个钟面(数字 0…11 ????)并显示一个拨动开关。切换开关将数字旋转 180 度,动画。或者这就是我的意图。但是不是每个数字都围绕数字圈走一条动画路径,而是每个数字沿着一条直线移动到它的新位置……所以数字圈塌陷到圆圈的中心点,然后重新爆炸成它的新配置:

animatableData 属性未被 SwiftUI 引用。知道为什么吗?

(请注意,使用标准的旋转动画是行不通的,因为这会使所有的数字都颠倒过来。)

import SwiftUI

struct ContentView: View {
    @State var state: Bool = false

    var body: some View {
        VStack {
            CircleView(hoursOffset: CGFloat(state ? 6 : 0 ))
                .aspectRatio(contentMode: .fit)
            Toggle(isOn: $state, label: { Text("Rotate 180°?") })
                .frame(maxWidth: 200).padding()
        }
    }
}

extension CGPoint {
    static func onCircle(hours: CGFloat, size: CGFloat) -> CGPoint {
        let radians = (3 - hours) * CGFloat.pi / CGFloat (6)
        let hypotenuse = size / 2
        return CGPoint(x: size / 2 + 0.8 * hypotenuse * cos(radians),
                       y: size / 2 - 0.8 * hypotenuse * sin(radians))
    }
}

struct CircleView: View {

    var hoursOffset: CGFloat

    public var animatableData: CGFloat {
        get { hoursOffset }
        set { self.hoursOffset = newValue }
    }

    var body: some View {
        GeometryReader() { geo in
            ForEach(0..<12) { hour in
                ZStack {
                    Text("\(hour)")
                        .position(CGPoint.onCircle(hours: CGFloat(hour) + self.hoursOffset, size: geo.size.width))
                        .animation(Animation.easeInOut(duration: 2.0))
                }
            }
        }
    }
}

【问题讨论】:

    标签: ios animation rotation swiftui


    【解决方案1】:

    这是可能的方法。使用 Xcode 11.4 / iOS 13.4 测试。仅在 View 中 animatableData 不起作用 - 您必须将其放入 AnimatableModifier

    struct CircleMoving: AnimatableModifier {
        var hoursOffset: CGFloat
        var hour: CGFloat
        var size: CGFloat
    
        public var animatableData: CGFloat {
            get { hoursOffset }
            set { self.hoursOffset = newValue }
        }
    
        func body(content: Content) -> some View {
            content
                .position(CGPoint.onCircle(hours: hour + hoursOffset, size: size))
        }
    }
    
    
    struct CircleView: View {
    
        var hoursOffset: CGFloat
    
        var body: some View {
            GeometryReader() { geo in
                ForEach(0..<12) { hour in
                    ZStack {
                        Text("\(hour)")
                            .modifier(CircleMoving(hoursOffset: self.hoursOffset, hour: CGFloat(hour), size: geo.size.width))
                    }
                }
            }
            .animation(.easeInOut(duration: 3.0), value: hoursOffset) // !!!
        }
    }
    

    可选:我还建议您将点值四舍五入以获得更好的完整性,但这并不重要。

    extension CGPoint {
        static func onCircle(hours: CGFloat, size: CGFloat) -> CGPoint {
            let radians = (3 - hours) * CGFloat.pi / CGFloat (6)
            let hypotenuse = size / 2
            return CGPoint(x: (size / 2 + 0.8 * hypotenuse * cos(radians)).rounded(.up),
                           y: (size / 2 - 0.8 * hypotenuse * sin(radians)).rounded(.up))
        }
    }
    

    【讨论】:

    • 谢谢你,@Asperi - 我很感激......再次!我去玩它。如果您愿意,请告诉我:您是如何了解 AnimatableModifier 的以及为什么它不起作用,例如,像我最初那样直接使用 onCircle(),而不是在修饰符中使用?我找不到任何文档,我很想了解,所以如果您有任何链接或提示要分享,我将非常感激!
    • 有什么想法吗,@Asperi?见上面的评论。谢谢!
    • @Anton, here 你可以找到好的开始材料,下面只是实验。 :)
    • 谢谢你,@Asperi。实际上,这就是我学到很多关于 AnimatableData 的地方——以及为什么在我看来,根据他的时钟示例,我不需要使用 AnimatableModifier(他不用于时钟)。它也是我找到的唯一一个 AnimatableModifier 参考资料。不幸的是,我没有发现他对 AnimatibleModifier 的解释对于理解为什么在这里需要它很有帮助。虽然他的例子很棒!
    • 这是累积动画的效果。通过在CircleView 中添加value: hoursOffset 进行修复。查看更新。
    【解决方案2】:

    问题是邪恶的数学......你正在计算端点。 Apple 只是将步骤从一个位置添加到另一个位置(作为一条线),所以这就是您所看到的。如果你从 0 切换到 1,它“看起来”像一个圆形移动,但它也是线性的;) 解决方案是,根据您的小时偏移值手动计算圆上的 x,y 位置

    【讨论】:

    • 谢谢你,@Chris。但恐怕我没明白你的意思。我定义的CGPoint.onCircle 函数确实计算了圆上的所有中间点,而不仅仅是端点,我定义的animatableData 属性指定所有这些中间值都应该在动画中使用。对吗?
    • 或者,@Chris,您可以分享您提供的屏幕截图的代码吗?
    猜你喜欢
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 2022-12-09
    相关资源
    最近更新 更多