0

Any idea why Field 3 and Field 5 do not get visible when active if the previous field was using the normal keyboard?

In the following code, if you tap on Field 1 and immediately after you tap on Field 3 or Field 5, they will not be visible; they get hidden by the keyboard.

Please note that Field 3 and Field 5 use the decimalPad keyboard while the rest of the fields use the standard keyboard.

  struct TextFieldScrollingIssue: View {
    @State private var testInput:String = ""
    
    var body: some View {
        VStack{
            Form {
                TextField("Field 1", text:$testInput)
                Spacer()
                Spacer()
                Spacer()
                Spacer()
                Spacer()
                Spacer()
                Spacer()
                Section(header: Text("Section 1: ")) {
                    TextField("Field 2", text:$testInput)
                    TextField("Field 3", text:$testInput)
                        .keyboardType(.decimalPad)
                }
                
                Section(header: Text("Section 2: ")) {
                    TextField("Field 4", text:$testInput)
                    TextField("Field 5", text:$testInput)
                        .keyboardType(.decimalPad)
                }
            }
        }
    }
}
fs_tigre
  • 10,650
  • 13
  • 73
  • 146
  • 1
    I only see the problem on simulators. On a couple of test devices, I can't recreate the problem. (ipad/16.1/landscape and iphone 12 pro max/ 16.2/portrait) – Marcy Jan 23 '23 at 00:16
  • Thank you for taking the time to test. I see the same issue in the simulator and in multiple devices, iPhone XS iOS16.1.2 and iPhone 13 iOS15.3. – fs_tigre Jan 23 '23 at 15:55
  • 1
    This seems like a bug that's worth filing with Apple, and it's not the first one I've seen with regards to the builtin keyboard scrolling. In UIKit we used to complain about writing boilerplate code to deal with the keyboard popping up and scrolling views, so in SwiftUI we get it for free but with bugs. My suggestion, build it in UIKit and live to fight another day. – Ron Srebro Jan 25 '23 at 18:41
  • 1
    The issue I saw with keyboard scrolling was when trying to show a sheet with a text field already in focus. In UIKit, the sheet will animate in together with the keyboard, but in SwiftUI, the sheet will animate fully, and then the keyboard will animate. It's a pretty terrible UX. – Ron Srebro Jan 25 '23 at 18:42
  • @RonSrebro - Just to be clear, are you suggesting creating the `TextFields` in `UIKit`? Thank you for your input. – fs_tigre Jan 25 '23 at 19:03
  • 1
    I'm suggesting creating the entire view in UIKit where you can have full control of how to scroll your view in response to the keyboard showing up. You'll find dozens of samples on how to make that work. Here's one https://stackoverflow.com/questions/1126726/how-can-i-make-a-uitextfield-move-up-when-the-keyboard-is-present-on-starting/1127025#1127025 If you're really committed to swiftUI, I would explore removing the Form view as I think that is probably where the problem is and implementing your own scrolling similar to @Yrb's answer – Ron Srebro Jan 25 '23 at 22:23

1 Answers1

1

I think the scrolling mechanism is confused because you used the same variable for all of the TextFields. Obviously, in production, you would never have this scenario. The fix is simple, use different variables:

struct TextFieldScrollingIssue: View {
    @FocusState var isFocused: String?
    @State private var testInput:String = ""
    @State private var decimalInput:String = ""
    
    var body: some View {
        VStack{
            ScrollViewReader { scroll in
                Form {
                    TextField("Field 1", text:$testInput)
                        .id("Field 1")
                        .focused($isFocused, equals: "Field 1")
                    Text(isFocused?.description ?? "nil")
                    Spacer()
                    Spacer()
                    Spacer()
                    Spacer()
                    Spacer()
                    Spacer()
                    Section(header: Text("Section 1: ")) {
                        TextField("Field 2", text:$testInput)
                            .id("Field 2")
                            .focused($isFocused, equals: "Field 2")
                        TextField("Field 3", text:$decimalInput)
                            .id("Field 3")
                            .focused($isFocused, equals: "Field 3")
                            .keyboardType(.decimalPad)
                    }
                    
                    Section(header: Text("Section 2: ")) {
                        TextField("Field 4", text:$testInput)
                            .id("Field 4")
                            .focused($isFocused, equals: "Field 4")
                        TextField("Field 5", text:$decimalInput)
                            .id("Field 5")
                            .focused($isFocused, equals: "Field 5")
                            .keyboardType(.decimalPad)
                    }
                }
                .onChange(of: isFocused) { _ in
                    if let isFocused = isFocused {
                        DispatchQueue.main.async {
                            withAnimation {
                                scroll.scrollTo(isFocused)
                            }
                        }
                    }
                }
            }
        }
    }
}

Edit:

Based on the comment, I was able to reproduce. Edited code to use a ScrollviewReader, @FocusState and view ids to correct.

Yrb
  • 8,103
  • 2
  • 14
  • 44
  • The variable is definitely not the issue, I'm having this issue in a real application where all variables are different, I also tried it in my original post sample with different variables and I still see the issue. By the way, in order for you to see the issue you need to add the spacers I have in my original post otherwise the keyboard will not cover the fields and it may seem like there is no issue. Thanks. – fs_tigre Mar 12 '22 at 19:06
  • 1
    I redacted them from my answer with the ellipsis. They are there in my code. It is working with no problem. Xcode 13.3RC, iOS 15.4. What are you seeing this on? Does it have to be a device? I suspect that you do not actually have a minimal reproducible example. What is your data model in your actual app? – Yrb Mar 12 '22 at 19:38
  • I see the issue in a device or the simulator, the code I'm showing is all my code no data models. Here is what I'm doing. With the code above, I tap on Field 1, at this point Field 4 and Field 5 are not visible since they are covered by the keyboard (this is important otherwise you will not see the issue), right after Field 1 I scroll up and tap on Field 5, here is the issue, Field 5 goes back to its original position, which was underneath the keyboard. I have tested this in multiple devices and multiple simulators. – fs_tigre Mar 13 '22 at 12:50
  • 1
    That would have been an important pice of information to put in your initial question. I can confirm that following these steps causes the issue. I would report this as a bug. I will work on trying to come up with a workaround. – Yrb Mar 13 '22 at 17:22
  • 1
    Changed code to correct for the issue brought up in comments. I would file a radar with Apple and put the FB# in comments. – Yrb Mar 13 '22 at 18:07
  • Please note that what I described in my previous comment was noted in the second paragraph of my original post. Sorry if it was confusing. Thanks a lot for the updated code. Is there a way to keep track of the radar from Apple? I would like to know when a fix is available. – fs_tigre Mar 14 '22 at 02:47
  • By the way, the only issue with your updated code is going from `Field 3` to `Field 4`, field 4 is hidden by the keyboard. – fs_tigre Mar 14 '22 at 02:54
  • @sideshowbarker - Hi, why was the answer from `Francis F` deleted? – fs_tigre Jan 25 '23 at 13:15