Toolbar Dismiss Button Does not Re-appear when Switching from one TabView to Another

I am brand new to SwiftUI and have made some good progress on my iOS app but I’ve come across an issue that I can’t get past. The app is organized into a TabView that contains two views – “SetupView” and “ColorsView”. The view where this issue resides is the SetupView.

SetupView is where the user enters some settings for the app. It consists of a NavigationStack, which contains a Form, which contains a section, which contains a TextField to hold the user’s zip code. There are other sections in the Form which are not text so there is no issue with those and I’ve omitted them from the code below for readability. I added the .keyboardType(.numberPad) modifier to the TextField since this is asking for a Zip Code. I also found I needed to track FocusState in order to add a “Dismiss” button to the keyboard so the user can get rid of it after entering a Zip Code. This all works fine within SetupView itself. But, when I tab over to ColorsView and then tab back to the Setup view and click on the TextField, the keyboard comes up but the “Dismiss” button is missing. How do I get the Dismiss button to show up every time I switch between Tabs and tap on the Zip code TextField? The relevant code is below. Note that the keyboard does not show up in the Canvas (Preview), but it does show up on the simulator or if you install the app on an iPhone.

BTW, an alternate solution to this would be if the keyboard went away after the user entered a 5 digit number (and of course, came back if the user taps on the TextField). This would avoid having to have the Dismiss button but I have no idea how to implement this alternative.

UPDATE: If I remove the NavigationStack from SetupView and just use a Form by itself, the problem seems to go away but only on the Canvas/Preview. On the device itself the same problem persists – the Dismiss button on the keyboard does not return after changing tabs.

UPDATE 2: I uploaded a quick video of what I’m seeing on my iPhone 13 Pro running iOS 17.0.3 here: https://youtu.be/Z2JNqwTkYFA

struct SetupView: View {
    
    @State var zipCode: String = ""
    @State var zipCodesOnly : [Int] = [33301, 90210, 33028, 90001]
    @State private var invalidZip = false
    @FocusState private var focusedField : FormField?
    enum FormField {
        case zip
    }
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Location")) {
                    TextField("Zip Code", text: $zipCode)
                        .keyboardType(.numberPad)
                        .focused($focusedField, equals: .zip)
                    Button("Save", action: {
                        if zipCodesOnly.contains(Int(zipCode) ?? 999989) {
                            invalidZip = false
                        } else {
                            invalidZip = true
                            zipCode = ""
                        }
                    })
                    .buttonStyle(.borderedProminent)
                    .tint(.accentColor)
                    .alert(isPresented: $invalidZip, content: {
                        Alert(title: Text("Invalid Zip Code"),
                              message: Text("Please enter a valid Zip Code"),
                              dismissButton: .default(Text("Ok"), action: {
                        }))
                    })
                }
            }
            .navigationTitle("Settings")
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Button("Dismiss") {. 
                        focusedField = nil
//keyboard with "Dismiss" comes up every time I tap on the Textfield while I'm in SetupView
//But I lose the "Dismiss" button when I Tab over to my other Tabview and come back to this one!
                    }
                }
            }
        }
    }
}

A sample Tab structure that can be used with this is below:

struct TempTabView: View {
    var body: some View {
        TabView {
            SetupView()
                .tabItem {
                    Image(systemName: "gearshape")
                    Text("Setup")
                }
            ColorsView()
                .tabItem {
                    Image(systemName: "paintpalette")
                    Text("Colors")
                }
        }
    }
}

#Preview {
    TempTabView()
}

And a sample ColorsView below:

struct ColorsView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

#Preview {
    ColorsView()
}

I’m using Xcode 15.0 with target iOS set to iOS 17.0

  • your code works fine with me as it is – keyboard with dismiss comes up also after I changed tabs (I’m running on macOS 14.0, Xcode 15.0, in simulator iOS 17.0)

    – 




  • Thanks for checking Chris but, that’s odd, I definitely do not get the Dismiss button above the keyboard when I do the following in the Simulator: Run the app above Click on the Textfield – keyboard with Dismiss button comes up. Enter a zip code (33301). Tap Dismiss button. Switch to Colors Tab. Switch back to Settings Tab. Tap TextField – keyboard comes up but there’s no Dismiss button. MacOS 13.6, Xcode 15.0, Simulator iOS 17.0

    – 




  • I also just noticed the following errors in the console: Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don’t want. Try this: (1) look at each constraint and try to figure out which you don’t expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you’re seeing NSAutoresizingMaskLayoutConstraints that you don’t understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)

    – 




  • More error info: “<NSAutoresizingMaskLayoutConstraint:0x6000021921c0 h=–& v=–& _UIToolbarContentView:0x116027f90.width == 0 (active)>”, “<NSLayoutConstraint:0x600002178320 H:|-(16)-[_UIButtonBarStackView:0x116034640] (active, names: ‘|’:_UIToolbarContentView:0x116027f90 )>”, “<NSLayoutConstraint:0x600002178410 H:[_UIButtonBarStackView:0x116034640]-(16)-| (active, names: ‘|’:_UIToolbarContentView:0x116027f90 )>” )

    – 

  • Even more error info: Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600002178410 H:[_UIButtonBarStackView:0x116034640]-(16)-| (active, names: ‘|’:_UIToolbarContentView:0x116027f90 )> And it goes on…

    – 

Leave a Comment