MattGreeny
MattGreeny

Reputation: 126

SwiftUI List Visual Bug with Keyboard and Safe Area

I'm experiencing a visual bug when using a List and TextFields in SwiftUI. After focusing on a TextField in the List and then removing the focus (I've tried various methods of doing this, like Buttons in the List rows/keyboard toolbar etc.), there is a visual bug where the black view behind the keyboard dismisses, hangs for a second over the safe area, then suddenly disappears, causing the animation not to appear smooth. Also, if you scroll to the bottom of the List and perform the same steps, after the black view lingers for a second, it then suddenly disappears again but causes the List to 'snap' down a bit and again ruins the animation.

Here is a minimal demonstration of the issue:

struct ContentView: View {
    
    @State var text: String = ""
    
    @FocusState private var focused: Int?
    
    var body: some View {
        List {
            ForEach(0..<50) { item in
                HStack {
                    TextField("", text: $text)
                        .id(item)
                        .focused($focused, equals: item)
                        .background(.white)
                    
                    Button {
                        focused = nil
                    } label: {
                        Text("Hide")
                    }
                }
            }
        }
        .scrollContentBackground(.hidden)
        .background(.red)
    }
}

One solution is using the List in a VStack with something below the List, but this is something I'd like to avoid with my UI. Also, I could use a ScrollView, but there is then a separate issue, where after dismissing the keyboard, the extra padding under the List produced by the keyboard avoidance stays there until you try and scroll the List again. I would also like to use the native swipe actions in my actual project. Finally, ignoring the safe area of the List/ScrollView works but then this disables keyboard avoidance, which is something that I'd like to keep.

Note: experienced on iOS 16.0 and 16.1

Upvotes: 8

Views: 2046

Answers (1)

bjorn.lau
bjorn.lau

Reputation: 1178

I am not 100% sure why this is happening, since I have not worked a lot with lists. But I guess it has something to do with that you are not explicitly telling your view to ignore the safe area. Adding .edgesIgnoringSafeArea(.bottom) to your list when leaving focus, and then changing it to trailing on entering focus seems to solve it.

struct ContentView: View {

    @State var text: String = ""
    @State var ignoreSafeArea = false

    @FocusState private var focused: Int?

    var body: some View {
        List {
            ForEach(0..<50) { item in
                HStack {
                    TextField("", text: $text)
                        .id(item)
                        .focused($focused, equals: item)
                        .background(.white)

                    Button {
                        focused = nil
                    } label: {
                        Text("Hide")
                    }
                }
            }
            .onChange(of: focused) { focused in
                if focused == nil {
                    ignoreSafeArea = true
                } else {
                    ignoreSafeArea = false
                }
            }
        }.edgesIgnoringSafeArea(ignoreSafeArea ? .bottom : .trailing)
    }
}

Upvotes: 1

Related Questions