我想要一个视图(下面的简化示例)通过重新绘制整个视图来实现值更改的动画效果,而不是快速默认的插补.将幻灯片从10移至100看起来不错.我希望动画也能做到这一点.

不是在所有视图中淡入淡出,而是在动画时间内分别绘制每个步骤.

我try 了有或没有关键帧和自定义过渡的自定义动画,但我无法获得我想要的结果.

import Foundation
import SwiftUI

struct SampleView: View {

    private let value: Int

    init(value: Double) {
        self.value = Int(max(min(value, 1.0), 0.0) * 100)
    }

    var body: some View {
        VStack(spacing: 0.0) {
            ForEach(0 ..< 101) { value in
                Rectangle()
                    .fill(color(for: value))
                    .frame(width: 100, height: 6.0)
                    .opacity(value < self.value ? 1.0 : 0.0)
            }
        }
    }

    private func color(for value: Int) -> Color {
        switch value % 4 {
        case 0: return Color.red
        case 1: return Color.blue
        case 2: return Color.yellow
        case 3: return Color.green
        default: return Color.black
        }
    }

}

#Preview("Interactive Sample") {
    struct InteractiveTestView: View {

        @State var value: Double = 0.1

        var body: some View {
            VStack {
                Text("\(Int(value * 100))%").font(.largeTitle)
                Button("Set to max") {
                    withAnimation(.easeInOut(duration: 5.0)) {
                        value = 0.9
                    }
                }
                Slider(value: $value, in: 0.0...1.0, step: 0.01).padding()
                SampleView(value: value)
            }
        }

    }

    return InteractiveTestView()
}

推荐答案

首先,我会将SampleViewInteractiveTestView都更改为value具有相同的语义,即value应该是一个介于0和SampleView之间的数字.

然后,你就可以从SampleViewAnimatable了.

struct InteractiveTestView: View {
    @State var value: Double = 10
    
    var body: some View {
        VStack {
            Text("\(Int(value))%").font(.largeTitle)
            Button("Set to max") {
                withAnimation(.easeInOut(duration: 5.0)) {
                    value = 99
                }
            }
            Slider(value: $value, in: 0.0...100.0, step: 1).padding()
            SampleView(value: value)
        }
    }
    
}
struct SampleView: View, Animatable {
    
    var value: Double
    
    var animatableData: Double {
        get { value }
        set { value = newValue }
    }
    
    var body: some View {
        VStack(spacing: 0.0) {
            ForEach(0 ..< 101) { value in
                let diff = self.value - Double(value)
                
                Rectangle()
                    .fill(color(for: value))
                    .frame(width: 100, height: 6.0)
                    .opacity(Double(value) < self.value ? 1.0 : 0.0)                        
            }
        }
    }
    
    private func color(for value: Int) -> Color {
        ...
    }
    
}

现在,矩形在动画过程中一个接一个地出现.

如果您还希望它们淡入,请使用opacity的连续函数:

.opacity(max(min(diff + 1, 1), 0))

Swift相关问答推荐

为表单部分赋予背景 colored颜色 /渐变

如何更新Square Order?

纹理资源加载问题(图像解码失败)

VisionOS发送通知

OBJC代码中Swift 演员的伊瓦尔:原子还是非原子?

如何在visionOS中定制悬停效果区域

可以';t在标记为@Observable的类上使用属性包装

Swift图表';chartXVisibleDomain挂起或崩溃

CardStack 和 Lottie

如何从 RC 加载场景作为 ModelEntity 而不是普通实体?

Firebase removeObserver 在 Swift 5 中不起作用

这是 Int64 Swift Decoding 多余的吗?

使 Picker 与其他 BinaryInteger 类型兼容

如何让不同的组件拥有不同的动画?

如何删除标签和步进按钮之间的间距是 SwiftUI Stepper?

使用 Date.ParseStrategy 将字符串解析为日期

Vapor - 流利的,将对象保存到 PostgreSQL

如何在 Swift 中复制字典?

从 NSData 对象在 Swift 中创建一个数组

'类 'className' 的 NSManagedObject 必须具有有效的 NSEntityDescription.错误