skip to Main Content

Here is the sample project source code: sample code

import SwiftUI

struct TestMenuInSafeAreaInset: View {
    @State private var message = ""

    var body: some View {
        VStack {
            Rectangle()
                .fill(Color.blue)
        }
            .safeAreaInset(edge: .bottom) {
                HStack {
                    TextField("Input your message", text: $message)
                        .padding()
                        .background(Color.brown)
                        .cornerRadius(12)
                        .foregroundColor(.white)
                    Menu {
                        Button {

                        } label: {
                            Text("Confirm")
                        }

                        Button {

                        } label: {
                            Text("Cancel")
                        }

                    } label: {
                        Image(systemName: "square.and.arrow.up.fill")
                            .tint(.white)
                            .padding()
                            .background(Color.brown)
                            .cornerRadius(50)
                    }
                }
                .padding()
            }
    }
}

struct TestMenuInSafeAreaInset_Previews: PreviewProvider {
    static var previews: some View {
        TestMenuInSafeAreaInset()
    }
}

When keyboard appear, the action menu button (right-bottom corner) shift up a little, and tap the menu will cause strange layout as the gif shown.

I think this is a bug, any solution to fix it? I test above code with iOS 16 and iOS 15 with same behaviour.

Normal state, it's ok

When keyboard shown first time, the right action menu button pushed little up.

[Updated 2022.10.10 11:31 +8]

As @Saket Kumar ‘s solution, I update the code as below, the issue is reproduced even I give size to the menu.

Test with iPhone 14 pro simulator iOS 16.

struct TestMenuInSafeAreaInset: View {
    @State private var message = ""

    var body: some View {
        VStack {
            TextField("Input your user name", text: $message)
                .padding()
                .background(Color.gray.opacity(0.3))
                .cornerRadius(12)
                .padding()
            Spacer()
        }
        .safeAreaInset(edge: .bottom) {
            HStack {
                Spacer()
                    .frame(height: 70)
                Menu {
                    Button {

                    } label: {
                        Text("Confirm")
                    }

                    Button {

                    } label: {
                        Text("Cancel")
                    }
                } label: {
                    Image(systemName: "square.and.arrow.up.fill")
                        .padding()
                        .tint(.white)
                        .background(Color.brown)
                        .cornerRadius(50)
                }
                .frame(width: 50, height: 50)
            }
            .padding(.horizontal, 20)
            .background(Color.blue)
        }
    }
}

The menu icon is shift up when first time keyboard shown up.

2

Answers


  1. Seems an issue with SwiftUI. I tested your code and I was able to reproduce the issue.

    I had my hunch that this weird behaviour may be caused by TextField’s Frame, and Menu’s frame overlapping, which SwiftUI is trying to accommodate.

    So I tried giving them frame manually, and it seems to fix the issue.

    Just change this portion of the code.

        .safeAreaInset(edge: .bottom) {
            HStack {
                TextField("Input your message", text: $message)
                    .padding()
                    .background(Color.brown)
                    .cornerRadius(12)
                    .foregroundColor(.white)
                    .frame(width: UIScreen.main.bounds.width*0.75, height: 50, alignment: .leading)
                Spacer()
                Menu {
                    Button {
    
                    } label: {
                        Text("Confirm")
                    }
    
                    Button {
    
                    } label: {
                        Text("Cancel")
                    }
    
                } label: {
                    Image(systemName: "square.and.arrow.up.fill")
                        .tint(.white)
                        .padding()
                        .background(Color.brown)
                        .cornerRadius(50)
                }.frame(width: UIScreen.main.bounds.width*0.10, height: 50, alignment: .trailing)
            }
            .padding()
        }
    

    Fair warning. Those frames I have put may not be exactly as you want. I have tested this code It works.

    enter image description here

    [Just added your code in demo project to simulate it]

    My suggestion would be to give fixed width, height to Menu say 60*60. Then take out 60 from the screen’s width, account for padding. And give suitable frame to TextField.

    Login or Signup to reply.
  2. Using UIButton and UIMenu seems to work around this issue without compromise, although it’s not ideal. It can be done via UIViewRepresentable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search