Reputation: 1490
I have a view that needs to work like an accordion where a user can drag to open/ collapse it. And I'd like it to not have any animation when the user is dragging, but animate while he leaves it. So, I thought of having two state variables(say, height and tempHeight) in my view, but I'd animate only when the height variable triggers the view rebuilding process.
Animating on both the height and tempHeight changes looks weird when dragging and feels like that there is a lag.
Sample code describing my problem:
struct ContentView: View {
@State var tempHeight: CGFloat = 200
@State var height: CGFloat = 200
var dragGesture: some Gesture {
DragGesture(),
.onChanged { value in
tempHeight = min(400, max(200, height + value.translation.height))
}
.onEnded { value in
height = tempHeight > 300 ? 400 : 200
tempHeight = height
}
}
var body: some View {
VStack {
Spacer()
.frame(height: 100)
Color.blue.frame(height: tempHeight)
.animation(.easeIn)
.gesture(dragGesture)
Spacer()
}
}
}
How I tried to work around this was to have a static var which keeps track of which state change happened last and it seems to work. My current workaround:
enum StateChange {
case height
case tempHeight
static var lastChangeBy: StateChange = .height
}
struct ContentView: View {
@State var tempHeight: CGFloat = 200
@State var height: CGFloat = 200
var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
tempHeight = min(400, max(200, height + value.translation.height))
StateChange.lastChangeBy = .tempHeight
}
.onEnded { value in
height = tempHeight > 300 ? 400 : 200
tempHeight = height
StateChange.lastChangeBy = .height
}
}
var body: some View {
VStack {
Spacer()
.frame(height: 100)
Color.blue.frame(height: tempHeight)
.gesture(dragGesture)
.animation(StateChange.lastChangeBy == .height ? .easeInOut : .none)
.animation(.easeInOut)
Spacer()
}
}
}
It feels like a hack to me. If I have many @State wrapped variables, it just becomes a mess quickly. Is it possible to know which state change triggers building the view inside the body, so that I can conditionally apply the animation to a particular trigger or a better way to write this.
Upvotes: 3
Views: 247
Reputation: 257789
You can specify explicitly which value changes activate animation in modifier, like
Color.blue.frame(height: tempHeight)
.gesture(dragGesture)
.animation(.easeInOut, value: height) // << here
Upvotes: 0