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

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

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

问题

Here's the translated code portion:

  1. class AnimatedCircle: ObservableObject, Hashable {
  2. public func hash(into hasher: inout Hasher) {
  3. hasher.combine(position.x)
  4. hasher.combine(position.y)
  5. }
  6. static func == (lhs: AnimatedCircle, rhs: AnimatedCircle) -> Bool {
  7. return lhs.position == rhs.position && lhs.width == rhs.width && lhs.opacity == rhs.opacity
  8. }
  9. internal init(opacity: Double = 1, width: CGFloat, position: CGPoint) {
  10. self.opacity = opacity
  11. self.width = width
  12. self.position = position
  13. }
  14. var opacity: Double
  15. var width: CGFloat
  16. let position: CGPoint
  17. }
  18. public struct SlidePresenterView: View {
  19. @State private var circles: [AnimatedCircle] = []
  20. public var body: some View {
  21. GeometryReader { geometry in
  22. ZStack {
  23. Color.black.ignoresSafeArea()
  24. }.onTapGesture { pointTapped in
  25. circles.append(
  26. AnimatedCircle.init(width: 40, position: pointTapped)
  27. )
  28. }
  29. ForEach(circles, id: \.self) { value in
  30. Circle()
  31. .stroke(style: StrokeStyle(lineWidth: 4))
  32. .frame(width: value.width)
  33. .foregroundColor(.white)
  34. .position(x: value.position.x, y: value.position.y)
  35. .opacity(value.opacity)
  36. .animation(.easeInOut(duration: 0.5)
  37. , value: value.opacity)
  38. .animation(.easeInOut(duration: 0.5)
  39. , value: value.width)
  40. .onAppear {
  41. value.opacity = 0
  42. value.width = 60
  43. }
  44. }
  45. }
  46. }
  47. }
  48. extension CGPoint: Hashable {
  49. public func hash(into hasher: inout Hasher) {
  50. hasher.combine(x)
  51. hasher.combine(y)
  52. }
  53. }

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

英文:
  1. class AnimatedCircle: ObservableObject, Hashable {
  2. public func hash(into hasher: inout Hasher) {
  3. hasher.combine(position.x)
  4. hasher.combine(position.y)
  5. }
  6. static func == (lhs: AnimatedCircle, rhs: AnimatedCircle) -> Bool {
  7. return lhs.position == rhs.position && lhs.width == rhs.width && lhs.opacity == rhs.opacity
  8. }
  9. internal init(opacity: Double = 1, width: CGFloat, position: CGPoint) {
  10. self.opacity = opacity
  11. self.width = width
  12. self.position = position
  13. }
  14. var opacity: Double
  15. var width: CGFloat
  16. let position: CGPoint
  17. }
  18. public struct SlidePresenterView: View {
  19. @State private var circles: [AnimatedCircle] = []
  20. public var body: some View {
  21. GeometryReader { geometry in
  22. ZStack {
  23. Color.black.ignoresSafeArea()
  24. }.onTapGesture { pointTapped in
  25. circles.append(
  26. AnimatedCircle.init(width: 40, position: pointTapped)
  27. )
  28. }
  29. ForEach(circles, id: \.self) { value in
  30. Circle()
  31. .stroke(style: StrokeStyle(lineWidth: 4))
  32. .frame(width: value.width)
  33. .foregroundColor(.white)
  34. .position(x: value.position.x, y: value.position.y)
  35. .opacity(value.opacity)
  36. .animation(.easeInOut(duration: 0.5)
  37. , value: value.opacity)
  38. .animation(.easeInOut(duration: 0.5)
  39. , value: value.width)
  40. .onAppear {
  41. value.opacity = 0
  42. value.width = 60
  43. }
  44. }
  45. }
  46. }
  47. }
  48. extension CGPoint: Hashable {
  49. public func hash(into hasher: inout Hasher) {
  50. hasher.combine(x)
  51. hasher.combine(y)
  52. }
  53. }

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:

  1. class TappedPoint: Hashable, ObservableObject {
  2. let position: CGPoint
  3. @Published var isVisible = true
  4. internal init(position: CGPoint) {
  5. self.position = position
  6. }
  7. public func hash(into hasher: inout Hasher) {
  8. hasher.combine(position.x)
  9. hasher.combine(position.y)
  10. }
  11. static func == (lhs: TappedPoint, rhs: TappedPoint) -> Bool {
  12. return lhs.position == rhs.position
  13. }
  14. func disappear() {
  15. isVisible = false
  16. }
  17. }
  18. struct AnimatedCircle: View {
  19. @ObservedObject var tappedPoint: TappedPoint
  20. var body: some View {
  21. Circle()
  22. .stroke(style: StrokeStyle(lineWidth: 4))
  23. .frame(width: tappedPoint.isVisible ? 40 : 60)
  24. .foregroundColor(.white)
  25. .position(x: tappedPoint.position.x, y: tappedPoint.position.y)
  26. .opacity(tappedPoint.isVisible ? 1.0 : 0.0)
  27. .onAppear {
  28. withAnimation(.easeInOut(duration: 0.5)) {
  29. tappedPoint.disappear()
  30. }
  31. }
  32. }
  33. }
  34. public struct SlidePresenterView: View {
  35. @State private var circles: [TappedPoint] = []
  36. public var body: some View {
  37. GeometryReader { geometry in
  38. ZStack {
  39. Color.black.ignoresSafeArea()
  40. ForEach(circles, id: \.self) { value in
  41. AnimatedCircle(tappedPoint: value)
  42. }
  43. }.onTapGesture { pointTapped in
  44. circles.append(
  45. TappedPoint(position: pointTapped)
  46. )
  47. }
  48. }
  49. }
  50. }

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:

确定