6

Since iOS 26, the toolbar on the keyboard isn't against the keyboard, there's a small gap as seen here on Safari: enter image description here

But in my app it shows up like this, without the gap: enter image description here

Here's my code:

.toolbar {
    ToolbarItem(placement: .keyboard) {
        HStack {
            Button(action: {
                switch focusedField {(...)}
            }) {
                Image(systemName: "chevron.up")
                    .padding()
            }
            .disabled(focusedField == .firstField)
            
            Button(action: {
                switch focusedField {(...)}
            }) {
                Image(systemName: "chevron.down")
                    .padding()
            }
            .disabled(focusedField == .lastField)
            
            Spacer()
            
            Button {
                focusedField = nil
            } label: {
                Image(systemName: "checkmark")
                    .padding()
            }
        }
    }
}

I tried using an ToolbarItemGroup instead, several ToolbarItem without the HStack, adding padding to any component or spacing in a VStack but that mainly just made the toolbar itself thicker. Is there a built-in Swift toolbar for the keyboard or am I doing something wrong?

Edit: With Benzy Neez' suggestion below, this code:

.toolbar {
    ToolbarItem(placement: .keyboard) {
        HStack {
            // ... content as before
            Button("", systemImage: "checkmark") {
                isFocused = false
            }
            .padding(5)
        }
        .background(.bar.opacity(0.5), in: .capsule)
        .glassEffect()
        .shadow(color: .black.opacity(0.2), radius: 20, y: 10)
        .padding(.bottom, 20)
    }
    .sharedBackgroundVisibility(.hidden)
}

appears with the gap but without the glass effect (my app on the left, safari on the right): enter image description here

1 Answer 1

3

If you hide the "shared background" around the toolbar item then it takes away the glass capsule. You can then apply a background of your own and style it as you like.

It seems that a height of about 48pt is reserved for the content in the ToolbarItem. So if the height of your content is less than this, there is a gap to the keyboard. By adding some padding, you can make the gap larger still.

For example:

.toolbar {
    ToolbarItem(placement: .keyboard) {
        HStack {
            // ... content as before
        }
        .background(.bar.opacity(0.5), in: .capsule)
        .glassEffect()
        .shadow(color: .black.opacity(0.2), radius: 20, y: 10)
        .padding(.bottom, 20)
    }
    .sharedBackgroundVisibility(.hidden)
}

Screenshot


EDIT As you pointed out in your comment and edit to the question, the container with the buttons (the HStack) does not have a glassy appearance when using this technique. My guess is that a .toolbar strips out the glass effects from the toolbar contents, because it normally applies its own glass effect around the complete toolbar. The modifier .sharedBackgroundVisibility is deliberately hiding the glass effect, so as to leave a gap. So by solving the problem of the gap, it has introduced a new problem of missing glass effect.

An alternative way to solve is to show a custom toolbar using .safeAreaBar. Glass effects can be applied to this content in the normal way.

Of course, the custom toolbar should only be shown when the keyboard is showing, so it requires some way of detecting when the keyboard is visible. The post How to detect if keyboard is present in swiftui provides some ways to do this.

Here is a standalone example to show it working. It uses the view modifier .detectKeyboard to update a flag when the keyboard is showing. This is a solution that I provided as one of the answers to the post mentioned.

struct ContentView: View {
    @State private var text = ""
    @State private var isKeyboardShowing = false

    var body: some View {
        Form {
            TextField("Text", text: $text)
        }
        .safeAreaBar(edge: .bottom) {
            if isKeyboardShowing {
                HStack {
                    Button {} label: {
                        Image(systemName: "chevron.up").padding()
                    }
                    Button {} label: {
                        Image(systemName: "chevron.down").padding()
                    }
                    Spacer()
                    Button {} label: {
                        Image(systemName: "checkmark").padding()
                    }
                }
                .buttonStyle(.plain)
                .glassEffect(.clear)
                .padding(.horizontal, 20)
                .padding(.bottom, 20)
            }
        }
        // See https://stackoverflow.com/a/79691042/20386264
        .detectKeyboard(isKeyboardShowing: $isKeyboardShowing)
        .animation(.default, value: isKeyboardShowing)
    }
}

Screenshot

This solution now looks right, but the animation is different to the default keyboard toolbar. Instead of moving with the keyboard, it fades in. So that's a new compromise to consider!

Sign up to request clarification or add additional context in comments.

4 Comments

This does create a gap but the glass effect isn't really there, it looks very flat unfortunately
It might look more glassy if there was some content behind it, especially when the content is scrolled.
Answer updated with an alternative approach.
That's much better indeed. Animation isn't perfect but at least it looks good, thank you for trying again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.