英文:
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 anObservableObject
. 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 theZStack
."
请注意,代码部分未翻译。
英文:
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 anObservableObject
. 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 theZStack
.
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)
)
}
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论