When animating with SwiftUI, how to set initial state while still animating from that state?

huangapple go评论63阅读模式
英文:

When animating with SwiftUI, how to set initial state while still animating from that state?

问题

Here's the translated code portion:

class AnimatedCircle: ObservableObject, Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(position.x)
        hasher.combine(position.y)
    }
    
    static func == (lhs: AnimatedCircle, rhs: AnimatedCircle) -> Bool {
        return lhs.position == rhs.position && lhs.width == rhs.width && lhs.opacity == rhs.opacity
    }
    
    internal init(opacity: Double = 1, width: CGFloat, position: CGPoint) {
        self.opacity = opacity
        self.width = width
        self.position = position
    }
    
    var opacity: Double
    var width: CGFloat
    let position: CGPoint
}

public struct SlidePresenterView: View {

@State private var circles: [AnimatedCircle] = []

public var body: some View {
    GeometryReader { geometry in
        ZStack {
            Color.black.ignoresSafeArea()
        }.onTapGesture { pointTapped in
            circles.append(
                AnimatedCircle.init(width: 40, position: pointTapped)
            )
            
        }
        ForEach(circles, id: \.self) { value in
            Circle()
                .stroke(style: StrokeStyle(lineWidth: 4))
                .frame(width: value.width)
                .foregroundColor(.white)
                .position(x: value.position.x, y: value.position.y)
                .opacity(value.opacity)
                .animation(.easeInOut(duration: 0.5)
                           , value: value.opacity)
                .animation(.easeInOut(duration: 0.5)
                           , value: value.width)
                .onAppear {
                    value.opacity = 0
                    value.width = 60
                }
        }
    }
}
}

extension CGPoint: Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }
}

If you have any further questions or need assistance with this code, please feel free to ask.

英文:
class AnimatedCircle: ObservableObject, Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(position.x)
        hasher.combine(position.y)
    }
    
    static func == (lhs: AnimatedCircle, rhs: AnimatedCircle) -> Bool {
        return lhs.position == rhs.position && lhs.width == rhs.width && lhs.opacity == rhs.opacity
    }
    
    internal init(opacity: Double = 1, width: CGFloat, position: CGPoint) {
        self.opacity = opacity
        self.width = width
        self.position = position
    }
    
    var opacity: Double
    var width: CGFloat
    let position: CGPoint
}

public struct SlidePresenterView: View {

@State private var circles: [AnimatedCircle] = []

public var body: some View {
    GeometryReader { geometry in
        ZStack {
            Color.black.ignoresSafeArea()
        }.onTapGesture { pointTapped in
            circles.append(
                AnimatedCircle.init(width: 40, position: pointTapped)
            )
            
        }
        ForEach(circles, id: \.self) { value in
            Circle()
                .stroke(style: StrokeStyle(lineWidth: 4))
                .frame(width: value.width)
                .foregroundColor(.white)
                .position(x: value.position.x, y: value.position.y)
                .opacity(value.opacity)
                .animation(.easeInOut(duration: 0.5)
                           , value: value.opacity)
                .animation(.easeInOut(duration: 0.5)
                           , value: value.width)
                .onAppear {
                    value.opacity = 0
                    value.width = 60
                }
        }
    }
}
}

extension CGPoint: Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }
}

In this example i show a circle when the user taps the screen, at the point they tap. Then the circle fades out. The only problem is that the animation only happens on the next tap, so the first circle has no animation, when the user taps second time, the first circle is animated out correctly, and so follows. Meaning a circle will permanent be on screen. There seems to be something wrong when setting the first value, and onAppear not correctly animating. Any ideas?

答案1

得分: 1

以下是您要翻译的部分:

"Perhaps the main reasons why it is not working is because the circles are not observing a particular instance of AnimatedCircle (they are created using an instance of AnimatedCircle, but this is not the same as observing an instance) and the opacity and width in AnimatedCircle are not published either.

I would suggest, you split your AnimatedCircle into data and a proper view of that data. The following illustrates how. The main changes:

  • The point of tap is now held by TappedPoint, which is an ObservableObject. It simply has a (published) flag to indicate whether the point should be visible or not.
  • The view of the point is the AnimatedCircle. This is where the opacity and width are controlled.
  • The visibility of the circle changes when the flag in the data changes, with animation.
  • Also, in SlidePresenterView, you probably intended the circles to be inside the ZStack."

请注意,代码部分未翻译。

英文:

Perhaps the main reasons why it is not working is because the circles are not observing a particular instance of AnimatedCircle (they are created using an instance of AnimatedCircle, but this is not the same as observing an instance) and the opacity and width in AnimatedCircle are not published either.

I would suggest, you split your AnimatedCircle into data and a proper view of that data. The following illustrates how. The main changes:

  • The point of tap is now held by TappedPoint, which is an ObservableObject. It simply has a (published) flag to indicate whether the point should be visible or not.
  • The view of the point is the AnimatedCircle. This is where the opacity and width are controlled.
  • The visibility of the circle changes when the flag in the data changes, with animation.
  • Also, in SlidePresenterView, you probably intended the circles to be inside the ZStack.

Here you go:

class TappedPoint: Hashable, ObservableObject {

    let position: CGPoint
    @Published var isVisible = true

    internal init(position: CGPoint) {
        self.position = position
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(position.x)
        hasher.combine(position.y)
    }

    static func == (lhs: TappedPoint, rhs: TappedPoint) -> Bool {
        return lhs.position == rhs.position
    }

    func disappear() {
        isVisible = false
    }
}

struct AnimatedCircle: View {

    @ObservedObject var tappedPoint: TappedPoint

    var body: some View {
        Circle()
            .stroke(style: StrokeStyle(lineWidth: 4))
            .frame(width: tappedPoint.isVisible ? 40 : 60)
            .foregroundColor(.white)
            .position(x: tappedPoint.position.x, y: tappedPoint.position.y)
            .opacity(tappedPoint.isVisible ? 1.0 : 0.0)
            .onAppear {
                withAnimation(.easeInOut(duration: 0.5)) {
                    tappedPoint.disappear()
                }
            }
    }
}

public struct SlidePresenterView: View {

    @State private var circles: [TappedPoint] = []

    public var body: some View {
        GeometryReader { geometry in
            ZStack {
                Color.black.ignoresSafeArea()
                ForEach(circles, id: \.self) { value in
                    AnimatedCircle(tappedPoint: value)
                }
            }.onTapGesture { pointTapped in
                circles.append(
                    TappedPoint(position: pointTapped)
                )
            }
        }
    }
}

huangapple
  • 本文由 发表于 2023年6月15日 02:12:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76476448.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定