SwiftUI 的 AnimatableModifier 现已弃用,但如何“直接使用 Animatable”呢?

Gwo*_*ozi 14 animation deprecated ios swiftui

我的目标是在偏移更改动画完成时实现回调。因此,我在网上找到了一种解决方法,它使用 来AnimatableModifier检查何时animatableData等于目标值。

struct OffsetAnimation: AnimatableModifier{
    typealias T = CGFloat
    var animatableData: T{
        get { value }
        set {
            value = newValue
            print("animating \(value)")
            if watchForCompletion && value == targetValue {
                DispatchQueue.main.async { [self] in onCompletion() }
            }
        }
    }
    var watchForCompletion: Bool
    var value: T
    var targetValue: T
    init(value: T, watchForCompletion: Bool, onCompletion: @escaping()->()){
        self.targetValue = value
        self.value = value
        self.watchForCompletion = watchForCompletion
        self.onCompletion = onCompletion
    }
    var onCompletion: () -> ()
    func body(content: Content) -> some View {
        return content.offset(x: 0, y: value).animation(nil)
    }
}

struct DemoView: View {
    @State var offsetY: CGFloat = .zero
    var body: some View {
        Rectangle().frame(width: 100, height: 100, alignment: .center)
            .modifier(
                OffsetAnimation(value: offsetY,
                                watchForCompletion: true,
                                onCompletion: {print("translation complete")}))
            .onAppear{
                withAnimation{ offsetY = 100 }
            }
        
    }
}
Run Code Online (Sandbox Code Playgroud)

但事实证明AnimatableModifier现在已被弃用。我找不到替代方案。

我知道这GeometryEffect适用于这种偏移量更改的情况,您可以用它ProjectionTransform来实现这一点。但我更关心官方推荐的“直接使用Animatable”。

Animatable说真的,我可以在网上找到的有关该协议的教程都使用Shape隐式实现该协议的结构Animatable的示例。我用协议即兴创作的以下代码Animatable甚至没有执行“动画”。

struct RectView: View, Animatable{
    typealias T = CGFloat
    var animatableData: T{
        get { value }
        set {
            value = newValue
            print("animating \(value)")
        }
    }
    var value: T
    var body: some View{
        Rectangle().frame(width: 100, height: 100, alignment: .center)
            .offset(y:value).animation(nil)
    }
}

struct DemoView: View{
    @State var offsetY: CGFloat = .zero
    var body: some View {
        RectView(value: offsetY)
            .onAppear{
                withAnimation{ offsetY = 100 }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢您的耐心阅读,也许还有即将到来的答案!

mys*_*elf 5

请在 iOS 15 及以上struct OffsetAnimationView: View, Animatable版本struct OffsetAnimation: AnimatableModifier中直接使用。

在iOS 15及以上版本中,我只使用继承andAnimatable来实现动画,没有使用or 。然而,在15以下的iOS版本中,我必须使用然后使用来应用它。对于应用程序中的每个动画,我使用这两种方法编写了两次。ViewAnimatableViewModifierAnimatableModifierAnimatableModifierColor.clear.modifier(...)

例如:

struct FooAnimatableModifier: AnimatableModifier {
    
    let oldValue: Int
    let newValue: Int
    
    var pct: CGFloat
    
    var animatableData: CGFloat {
        get { pct }
        set { pct = newValue }
    }
    
    var currentValue: CGFloat {
        CGFloat(oldValue) + (CGFloat(newValue) - CGFloat(oldValue)) * pct
    }
    
    func body(content: Content) -> some View {
        FooStaticView(value: currentValue)
    }
    
}

struct FooAnimatableView: View, Animatable {
    
    let oldValue: Int
    let newValue: Int
   
    var pct: CGFloat
    
    var animatableData: CGFloat {
        get { pct }
        set { pct = newValue }
    }
    
    var currentValue: CGFloat {
        CGFloat(oldValue) + (CGFloat(newValue) - CGFloat(oldValue)) * pct
    }
    
    var body: some View {
        FooStaticView(value: currentValue)
    }
    
    
}

struct FooStaticView: View {
    
    var value: CGFloat
    
    var body: some View {
        Text("\(value)")
    }
    
}

struct FooView: View {
    
    @State var isAnimated = false
    
    var body: some View {
        Group {
            if #available(iOS 15, *) {
                FooAnimatableView(oldValue: 20, newValue: 80, pct: isAnimated ? 1 : 0)
            } else {
                EmptyView().modifier(
                    FooAnimatableModifier(oldValue: 20, newValue: 80, pct: isAnimated ? 1 : 0)
                )
            }
        }
        .animation(.linear(duration: 0.5), value: isAnimated)
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                isAnimated = true
            }
        }
    }
    
}
Run Code Online (Sandbox Code Playgroud)

避免在 iOS 13.2 以下的容器(例如 VStack)中使用它。

  • 对我来说也不起作用。如果我直接使用 AnimatableModifier 那么动画就会起作用。如果我使用 Animatable、ViewModifier,则动画不起作用。 (2认同)