This SwiftUI animation should only fade out. Why does it move to the right?

I'll answer my own question... It turns out, the parent view shrinks during the transition, making the text view to move with it. To illustrate, I added some borders to the views:

enter image description here

In order to solve the problem, I must ensure the parent view does not shrink. It was as simple as adding this:

HStack { Spacer() }

enter image description here

The modified code would look like this:

import SwiftUI

struct ContentView : View {
    @State private var showText = true
    
    var body: some View {
        VStack {
            Spacer()
            
            if showText {
                Text("I should always be centered!")
                    .font(.largeTitle)
                    .transition(.opacity)
                    .border(Color.blue)
            }
            
            Spacer()
            
            Button {
                withAnimation(.basic(duration: 1.5)) {
                    self.showText.toggle() 
                }
            } label: {
                Text("CHANGE")
                    .font(.title)
                    .border(Color.blue)
            }
            
            Spacer()
            
            // This ensures the parent is kept wide to avoid the shift.
            HStack { Spacer() }   
        }
        .border(Color.green)
    }
}

I still think this is a bug, otherwise, the fade-in should have the same behavior, and it doesn't. If this is not a bug, it is not what one would expect. I'll file a bug report.


SwiftUI animates layout changes automatically.

My theory is that a Spacer replaces the Text when you hide it, and by expanding to fill the superview, it pushes out the Text towards the trailing edge of the screen.

You can fix the issue with this approach:

Text("I should always be centered!")
                .font(.largeTitle)
                .opacity(showText ? 1 : 0)

The Text will fade in/out without moving this way.

Tags:

Swift

Swiftui