Reputation: 21883
I have this strange animation bug I've not been able to figure out. When transitioning on a view that contains a slider, the slider appears to animate independently of the rest of the view.
For reference, here is the code that animates the two views:
@main
struct TransitionAnimationBug: App {
@State var displaySettings = false
var body: some Scene {
WindowGroup {
Group {
if displaySettings {
SliderView(displaySettings: $displaySettings)
.transition(AnyTransition.asymmetric(
insertion: .move(edge: .trailing),
removal: .move(edge: .leading)
))
} else {
MainView(displaySettings: $displaySettings)
.transition(AnyTransition.asymmetric(
insertion: .move(edge: .leading),
removal: .move(edge: .trailing)
))
}
}
.animation(.easeInOut, value: displaySettings) // Shows bug
// .animation(.spring(), value: displaySettings) // Works
}
}
}
And here is the code from the settings screen that displays the slider:
struct SliderView: View {
var displaySettings: Binding<Bool>
@State var sliderValue: Double = 4.0
init(displaySettings: Binding<Bool>) {
self.displaySettings = displaySettings
}
var body: some View {
VStack(spacing: 30) {
Text("Hello, world!")
Slider(value: $sliderValue, in: 0 ... 4, step: 1)
.border(.red)
.padding(20)
Button("Close") { displaySettings.wrappedValue.toggle() }
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.2))
}
}
And for completeness the main view:
struct MainView: View {
var displaySettings: Binding<Bool>
init(displaySettings: Binding<Bool>) {
self.displaySettings = displaySettings
}
var body: some View {
VStack(spacing: 30) {
Text("Hello, world!")
.padding()
Button("Show transition bug") {
displaySettings.wrappedValue.toggle()
}
Text("The slider will animate incorrectly")
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.white)
}
}
As you can see this is a minimal app I built to show the bug (which I've submitted to Apple via their Feedback assistant), but I was wondering if anyone here as encountered it and found a solution.
Through experimenting I found that using a .spring()
animation instead of .slideInOut
correctly animates the slider, but any other animation fails.
Any thoughts?
Upvotes: 1
Views: 297
Reputation: 21883
Thanks Asperi, that worked. Do you know why? On first glance there's no obvious reason why it would work other than it being something to do with the fact that the body
returns a Scene
as opposed to a View
.
The changed code looks like this:
@main
struct TransitionAnimationBug: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var displaySettings = false
var body: some View {
Group {
if displaySettings {
SliderView(displaySettings: $displaySettings)
.transition(AnyTransition.asymmetric(
insertion: .move(edge: .trailing),
removal: .move(edge: .leading)
))
} else {
MainView(displaySettings: $displaySettings)
.transition(AnyTransition.asymmetric(
insertion: .move(edge: .leading),
removal: .move(edge: .trailing)
))
}
}
.animation(.easeInOut, value: displaySettings)
}
}
Upvotes: 0
Reputation: 257779
This is effect of changing root view of window. The fix is to move the content of WindowGroup
with all related states into separated view (to make root of window persistent), so it looks like
WindowGroup {
ContentView() // << everything else move here !!
}
Tested with Xcode 13.3 / iOS 15.4
Upvotes: 1