I have a List
that's displaying two Views
of the same type. When you tap on one of the views, they change their height with an animation.
However, the List
those views are embedded in doesn't animate which results in an ugly glitch because the height of the List
row changes instantly, while the actual view inside that row is animated:
How can I make the List
animate as well? I tried addinga .animation
modifier to it but that doesn't do anything.
I also don't want to move the tapGesture
out of the view. The view should be self-contained and not rely on some other view to control it (I think that's what MVVM is about)
thanks!
import SwiftUI
struct SubView: View {
@State var change: Bool = false
var body: some View {
Rectangle()
.frame(width: 200, height: change ? 300 : 200)
.foregroundColor(Color.red)
.onTapGesture {
withAnimation {
self.change.toggle()
}
}
}
}
struct Test: View {
var body: some View {
List {
SubView()
SubView()
}
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
The solution is just to make height animatable continuously, by providing explicit animatable modifier for this.
Here is working approach. Tested with Xcode 11.4 / iOS 13.4.
Implementation of simple helper modifier
struct AnimatingCellHeight: AnimatableModifier {
var height: CGFloat = 0
var animatableData: CGFloat {
get { height }
set { height = newValue }
}
func body(content: Content) -> some View {
content.frame(height: height)
}
}
Modified using view (other parts unchanged)
struct SubView: View {
@State var change: Bool = false
var body: some View {
Rectangle()
.frame(width: 200)
.modifier(AnimatingCellHeight(height: change ? 300 : 200))
.foregroundColor(Color.red)
.onTapGesture {
withAnimation {
self.change.toggle()
}
}
}
}