8

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

8
  • Hi Matt, I ran into the same issue even on iOS 17. Have you filed a feedback with Apple, and have you found any workaround since your post? Thanks for detailing this bug! Commented Oct 3, 2023 at 14:15
  • Reported as FB13228246. Commented Oct 3, 2023 at 14:55
  • Hey Lukáš! Thanks a lot for filing this with Apple - I hadn't done this (but probably should have!). It's not ideal, but my solution was to ignore the keyboard safe area, which fixes the visual bug but disables keyboard avoidance, and resort to using IQKeyboardManager. While I'd rather not have to use it, it made all my problems magically disappear and is a very easy solution, so I'd highly recommend giving it a go if it suits you. Commented Oct 3, 2023 at 18:20
  • 1
    It's iOS 17.3 and the issue is still here. Extremely annoying. It becomes even more noticeable when trying to add .keyboard Toolbar to show above the keyboard. SwiftUI adding additional safeArea for it, an it too glitches with empty space ruining the animation :( Commented Jan 28, 2024 at 22:39
  • 1
    this issue still persists in 17.4. Did they ever reach out to you @LukášKubánek Commented Apr 30, 2024 at 17:58

1 Answer 1

1

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)
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

This does work, but the only problem is that this then disables the keyboard avoidance of the TextFields, unless you know a way I can prevent this and still ignore the safe area? I can, of course, implement my own keyboard avoidance, but I'm experiencing other issues (e.g. here) and would rather use the native solution
Well, that's true. I did not consider that could be a problem. I updated my answer with another solution that should maintain the keyboard avoidance and keep the animation smooth.
Thanks a lot for updating your solution - this is definitely a step in the right direction. The only problem that seems to remain is with the black view behind the keyboard. With your solution, it still seems to 'lag behind' when dismissing (although no longer sticks around over the bottom safe area) and suddenly appears when focusing on a TextField before the keyboard has chance to appear, which doesn't look great. I've updated my question to add a red background to the List which makes the issue clear. Do you know how I can get around this?

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.