4

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.

ZYiOS
  • 5,204
  • 3
  • 39
  • 45

2 Answers2

0

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.

Saket Kumar
  • 1,157
  • 2
  • 14
  • 30
0

Using UIButton and UIMenu seems to work around this issue without compromise, although it's not ideal. It can be done via UIViewRepresentable.

Ben Guild
  • 4,881
  • 7
  • 34
  • 60