swiftUI动画:使类符合VectorArithmetic的animatableData用法。

问题描述 投票:0回答:1

我试着做了一个自己的结构,把它用在AnimatableModifier的animatableData属性中。这很顺利。然后我试着做一个符合VectorArithmetic的类,结果失败了。

很顺利,动画在运行,但是里面没有插值。一直到动画显示我的最后一个位置,我搞不懂为什么。看来SwiftUI不能存储变化,认为动画开始前和动画结束后的位置是相等的。

这里是完整的代码。

import SwiftUI

struct SimpleBorderMove: View{
    var body: some View{
         SimpleView()
            .frame(height: 300)
    }
}

struct SimpleView: View{
    @State var position: CGFloat = 0
    @State var height: CGFloat = 0
    var body: some View{
        VStack{
            ZStack{
                Rectangle()
                    .fill(Color.gray)
                BorderView(position: position, height: height)
            }
            HStack{
                VStack{
                    HStack{
                        Slider(value: self.$position, in: 0...1)
                        Button(action: {
                            withAnimation(.linear(duration: 1)){
                                self.position = 0
                            }
                        }){
                            Text("X")
                        }
                    }
                    HStack{
                        Slider(value: self.$height, in: 0...1)
                        Button(action: {
                            withAnimation(.linear(duration: 1)){
                                self.height = 0
                            }
                        }){
                            Text("X")
                        }
                    }
                }
                Button(action: {
                    withAnimation(.linear(duration: 1)){
                        self.position = CGFloat.random(in: 0..<1)
                        print("new position is \(self.position)")
                        self.height = CGFloat.random(in: 0..<1)
                        print("new height is \(self.height)")
                    }
                }){
                    Text("Randomize")
                }.background(Color.gray)

            }
        }
    }
}


struct BorderView: View{
    public var animatableData: CGFloat {
        get {
            print("Reding position: \(position)")
            return self.position
        }
        set {
            self.position = newValue
            print("setting position: \(position)")
        }
    }
    var position: CGFloat
    var height: CGFloat
    let borderWidth: CGFloat
    init(position: CGFloat, borderWidth: CGFloat = 10, height: CGFloat = 1){
        self.position = position
        self.borderWidth = borderWidth
        self.height = height
        print("BorderView init")
    }
    var body: some View{
        Rectangle()
           .fill(Color.green)
           .frame(width: self.borderWidth)
           .twoParameterBorder(position: position, height: height)
    }
}

struct BorderPosition: AnimatableModifier {//ViewModifier
    var position: CGFloat
    let startDate: Date = Date()
    public var animatableData: CGFloat {
        get {
            print("animation: reading position: \(position) at time \(Date().timeIntervalSince(startDate))")
            return position
        }
        set {
            print("animation: setting position: \(newValue) at time \(Date().timeIntervalSince(startDate))")
            position = newValue
        }
    }

    init(position: CGFloat){
        self.position = position
        print("modifier init with position \(position)")
    }
    func body(content: Content) -> some View {
        GeometryReader{geometry in
            content
            .animation(nil)
            .offset(x: self.getXOffset(inSize: geometry.size), y: 0)
        }
    }
    func getXOffset(inSize: CGSize) -> CGFloat{
        let p = position
        let offset = -inSize.width / 2 + inSize.width * p
        print("at position  \(p) offset is \(offset)")
        return offset
    }
}

extension View{
    func borderIn(position: CGFloat) -> some View{
        self.modifier(BorderPosition(position: position))
    }
}
extension View{
    func twoParameterBorder(position: CGFloat, height: CGFloat) -> some View{
        self.modifier(TwoParameterBorder(position: position, height: height))
    }
}

struct TwoParameterBorder: AnimatableModifier {
    var height: CGFloat
    var position: CGFloat
    let startDate: Date = Date()
    public var animatableData: MyAnimatableVector{
        get {
            print("animation read position: \(position), height: \(height) at time: \(Date().timeIntervalSince(startDate))")
            return MyAnimatableVector(position: position, height: height)
        }
        set {
            self.position = newValue.position
            print("animating position at \(position) at time: \(Date().timeIntervalSince(startDate))")
            self.height = newValue.height
            print("animating height at \(height) at time: \(Date().timeIntervalSince(startDate))")
        }
    }

    init(position: CGFloat, height: CGFloat){
        self.position = position
        self.height = height
    }

    init(height: CGFloat, position: CGFloat){
        self.position = position
        self.height = height
    }
    func body(content: Content) -> some View {
       GeometryReader{geometry in
            content
                .animation(nil)
                .offset(x: -geometry.size.width / 2 + geometry.size.width * self.position, y: 0)
                .frame(height: self.height * (geometry.size.height - 20) + 20)
        }
    }
}

final class  MyAnimatableVector: VectorArithmetic{
//struct MyAnimatableVector: VectorArithmetic{
    var position: CGFloat
    var height: CGFloat

    static func - (lhs: MyAnimatableVector, rhs: MyAnimatableVector) -> Self {
        var new = Self.init()
        new.position = lhs.position - rhs.position
        new.height = lhs.height - rhs.height
        print("\(lhs.position) - \(rhs.position) = \(new.position)")
        print("\(lhs.height) - \(rhs.height) = \(new.height)")
        return new
    }

    static func -= (lhs: inout MyAnimatableVector, rhs: MyAnimatableVector) {
        lhs = lhs - rhs
    }

    static func + (lhs: MyAnimatableVector, rhs: MyAnimatableVector) -> Self {
        var new = Self.init()
        new.position = lhs.position + rhs.position
        new.height = lhs.height + rhs.height
        print("\(lhs.position) + \(rhs.position) = \(new.position)")
        print("\(lhs.height) + \(rhs.height) = \(new.height)")
        return new
    }
//
    static func += (lhs: inout MyAnimatableVector, rhs: MyAnimatableVector) {
        lhs = lhs + rhs
    }

    //mutating
    func scale(by rhs: Double) {
        let newPosition = self.position * CGFloat(rhs)
        let newHeight = self.height * CGFloat(rhs)
        print("\(position) * \(rhs) = \(newPosition)")
        print("\(height) * \(rhs) = \(newHeight)")
        self.position = newPosition
        self.height = newHeight
    }

    var magnitudeSquared: Double{
        get{
            let result =  Double(self.position * self.position) + Double(self.height * self.height)
            return result
        }
    }

    static var zero: Self{
        get{Self.init()}
    }

    static func == (lhs: MyAnimatableVector, rhs: MyAnimatableVector) -> Bool {
        let result = lhs.position == rhs.position && lhs.height == rhs.height
        return result
    }


    init(position: CGFloat, height: CGFloat){
        self.position = position
        self.height = height
    }
    required init(){
        self.position = 0
        self.height = 0
    }
}

struct SimpleBorderMove_Previews: PreviewProvider {
    static var previews: some View {
        SimpleBorderMove()
            .frame(height: 300)
    }
}

和结构体的实现完全一样,效果很好。

//final class  MyAnimatableVector: VectorArithmetic{
struct MyAnimatableVector: VectorArithmetic{
    var position: CGFloat
    var height: CGFloat

    static func - (lhs: MyAnimatableVector, rhs: MyAnimatableVector) -> Self {
        var new = Self.init()
        new.position = lhs.position - rhs.position
        new.height = lhs.height - rhs.height
        print("\(lhs.position) - \(rhs.position) = \(new.position)")
        print("\(lhs.height) - \(rhs.height) = \(new.height)")
        return new
    }

    static func -= (lhs: inout MyAnimatableVector, rhs: MyAnimatableVector) {
        lhs = lhs - rhs
    }

    static func + (lhs: MyAnimatableVector, rhs: MyAnimatableVector) -> Self {
        var new = Self.init()
        new.position = lhs.position + rhs.position
        new.height = lhs.height + rhs.height
        print("\(lhs.position) + \(rhs.position) = \(new.position)")
        print("\(lhs.height) + \(rhs.height) = \(new.height)")
        return new
    }
//
    static func += (lhs: inout MyAnimatableVector, rhs: MyAnimatableVector) {
        lhs = lhs + rhs
    }

    mutating
    func scale(by rhs: Double) {
        let newPosition = self.position * CGFloat(rhs)
        let newHeight = self.height * CGFloat(rhs)
        print("\(position) * \(rhs) = \(newPosition)")
        print("\(height) * \(rhs) = \(newHeight)")
        self.position = newPosition
        self.height = newHeight
    }

    var magnitudeSquared: Double{
        get{
            let result =  Double(self.position * self.position) + Double(self.height * self.height)
            return result
        }
    }

    static var zero: Self{
        get{Self.init()}
    }

    static func == (lhs: MyAnimatableVector, rhs: MyAnimatableVector) -> Bool {
        let result = lhs.position == rhs.position && lhs.height == rhs.height
        return result
    }


    init(position: CGFloat, height: CGFloat){
        self.position = position
        self.height = height
    }
   // required
    init(){
        self.position = 0
        self.height = 0
    }
}

我在这个类上做错了什么?

swift swiftui protocols core-animation final
1个回答
0
投票

你的类没有做错什么--只是苹果只对结构支持这个功能。

© www.soinside.com 2019 - 2024. All rights reserved.