Sam
Sam

Reputation: 11

How to prevent view items from shifting when item is hidden in SwiftUI

I am coding a SwiftUI view file that prompts the user to enter their date of birth. After all fields have been appropriately entered a navigation button appears to navigate to the next step. My only problem is when the button appears it shifts all the text included in the HStack to the left. How do I add the button without shifting the text? I tried making a separate file for the button and called that file instead but it made all the text disappear.

I tried using spacers, it makes the text shift less but I don't want it to shift at all.

(I am very new to Swift any help is greatly appreciated)

HStack {
    Spacer(minLength: 50)
    Spacer(minLength:10)
                
    VStack(alignment: .leading) {
        Text("Enter")
            .font(Font.custom("Staatliches-Regular", size: 30))
            .foregroundColor(Color.white)
            .kerning(7.2)
        
        Text("Your")
             .font(Font.custom("Staatliches-Regular", size: 30))
             .foregroundColor(Color.white)
             .kerning(7.2)
             
        Text("Birthday")
             .font(Font.custom("Staatliches-Regular", size: 30))
             .foregroundColor(Color.white)
             .kerning(7.2)
    }
    .padding(.leading, -180)
    .padding(.bottom, 100)
    .padding()
            
    Spacer()
            
    if !day.isEmpty && !month.isEmpty && !year.isEmpty {
        Button(action: {
            // Action when the button is tapped
        }) {
               Image(systemName: "arrow.right.circle.fill")
                   .resizable()
                   .frame(width: 50, height: 50).foregroundColor(Color.white)
            }
    }
    Spacer()
}

Upvotes: 0

Views: 661

Answers (2)

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119917

You can simply make it invisible using the opacity instead of the hidden modifier:

let shouldShow = day.isEmpty && !month.isEmpty && !year.isEmpty

Button(action: {
    // Action when the button is tapped
}) {
    Image(systemName: "arrow.right.circle.fill")
        .resizable()
        .frame(width: 50, height: 50)
        .foregroundColor(Color.white)
}
.opacity(shouldShow ? 1 : 0) 👈 Prevent user to see it with their eyes
.accessibilityHidden(!shouldShow) 👈 Prevent voice over and others to see it

More info in this detailed answer

Upvotes: 0

Usukhbayar Batbayar
Usukhbayar Batbayar

Reputation: 1265

Seems it's very hard to achieve because Spacer() and HStack are dynamically changing the alignment.

However, we could solve it by making the button invisible and untouchable when conditions aren't met. This way, the items' alignment stay fixed in the same position; No movement at all when button appear/dissapear.

@State private var showButton = false
    
    var body: some View {
        HStack {
            Spacer(minLength: 50)
            Spacer(minLength:10)
            
            VStack(alignment: .leading) {
                Text("Enter")
                    .font(Font.custom("Staatliches-Regular", size: 30))
                    .foregroundColor(Color.white)
                    .kerning(7.2)
                
                Text("Your")
                    .font(Font.custom("Staatliches-Regular", size: 30))
                    .foregroundColor(Color.white)
                    .kerning(7.2)
                
                Text("Birthday")
                    .font(Font.custom("Staatliches-Regular", size: 30))
                    .foregroundColor(Color.white)
                    .kerning(7.2)
            }
            .padding(.leading, -180)
            .padding(.bottom, 100)
            .padding()
            
            Spacer()
            
            showButton = !day.isEmpty && !month.isEmpty && !year.isEmpty

            Button(action: {
                // Action when the button is tapped

            }) {
                Image(systemName: "arrow.right.circle.fill")
                    .resizable()
                    .frame(width: 50, height: 50).foregroundColor(Color.white)
            }
            .opacity(showButton ? 1 : 0) // Make it invisibile
            .allowsHitTesting(showButton) // Disable user interaction 
            
            Spacer()
        }
    }

Hope it helps.

Upvotes: 1

Related Questions