1

The Context:

I have a List row that, in some cases, shows a TextField (depending on a switch condition). I managed to solve the focus problem with an excellent article from Peter Friese Link to article that suggests a very clever use of enums Associated fields.

The Issue:

BUT I'm facing an inconsistent scrolling behavior that can be seen in this screen capture:

iOS sim capture os scrolling issue

The relevant code:

            List($roundQuestions) { $question in
                HStack {
                    Group {
                        Image(systemName: "\(question.index ).circle")
                            .foregroundColor(questionColor(question: question))
                            .scaleEffect(0.6)
                        Text("\(question.factorA)")
                        Text("x")
                        question.status == .unrevealed ? Text("?") : Text("\(question.factorB)")
                        Text("=")
                        switch question.status {
                        case .unrevealed:
                            if question.index == currentQuestionArrayIndex + 1 {
                                Spacer()
                                Button("Go!") {
                                    question.status = .active
                                }
                                .buttonStyle(.borderedProminent)
                                .font(.none)
                                .lineLimit(1)
                            }
                        case .active:
                            ZStack (alignment: .trailing) {
                                TextField("??", text: $questionGuess )
                                    .textFieldStyle(.roundedBorder)
                                    .keyboardType(.numberPad)
                                    .focused($focusedQuestion, equals: .row(id: question.id))
                                    .onAppear {
                                        focusedQuestion = .row(id: question.id)
                                        print("onAppear question.id = \(question.index)")
                                    }
                                    .onDisappear {
                                        print("onDisappear question.id = \(question.index)")
                                    }
                            }
                            
                            Button("?") {
                                processUserGuess()
                            }
                            .buttonStyle(.borderedProminent)
                            .font(.none)
                        case .error:
                            Text("\(question.productGuess)").foregroundColor(.red)
                        case .right:
                            Text("\(question.productGuess)").foregroundColor(.green)
                        default:
                            Text("__")
                        }
                    }
                    .font(.custom("SF Compact", size: 40, relativeTo: .largeTitle))
                    .padding(.vertical)
                }
            }

The entire repo (for those who goes deep):

https://github.com/gilsoncav/tabulenzo

Observation #1:

I'm aware of solution paths like Move TextField up when the keyboard has appeared in SwiftUI but they seem "hacky" and the code doesn't "read well" in my opinion

Observation #2:

I'm a Swift, native iOS and SwiftUI newbie.

Gilson Cavalcanti
  • 1,483
  • 1
  • 12
  • 18
  • Does [this](https://stackoverflow.com/a/69500827/7129318) answer your question? See the bottom code. There is nothing built in that handles it. – Yrb Feb 24 '22 at 12:54
  • It was VERY useful your appointed answer. Thanks a lot. I ended up delaying the scrolling due to the keyboard appearance animation. It looks like the scroll gets the view not yet shrinked by the keyboard in my case. – Gilson Cavalcanti Feb 25 '22 at 02:26
  • There is a delay built in to the answer in the `.onChange(of: keyboardVisible, perform: { _ in` of 0.5 seconds. You can adjust that to whatever you need to make it work. That is one of the drawbacks to the answer. You have to wait until the keyboard is fully up before fixing the view. – Yrb Feb 25 '22 at 02:31

1 Answers1

2

There is another option, but it uses ScrollView instead of List, and then facilitates ScrollViewReader to scroll to the active question. It works, but you would have to do some custom formatting to get the same look as before (I already added some Spacers to get the overall look):

            // Scrollview & reader instead of List
            ScrollViewReader { scrollProxy in
                ScrollView {
                    ForEach($roundQuestions) { $question in
                        HStack {
                            Group {
                                Image(systemName: "\(question.index ).circle")
                                    .foregroundColor(questionColor(question: question))
                                    .scaleEffect(0.6)
                                Text("\(question.factorA)")
                                Text("x")
                                question.status == .unrevealed ? Text("?") : Text("\(question.factorB)")
                                Text("=")
                                switch question.status {
                                case .unrevealed:
                                    if question.index == currentQuestionArrayIndex + 1 {
                                        Spacer()
                                        Button("Go!") {
                                            question.status = .active
                                        }
                                        .buttonStyle(.borderedProminent)
                                        .font(.none)
                                        .lineLimit(1)
                                    } else {
                                        Spacer()
                                    }
                                case .active:
                                    HStack  {
                                        TextField("??", text: $questionGuess )
                                            .textFieldStyle(.roundedBorder)
                                            .keyboardType(.numberPad)
                                            .focused($focusedQuestion, equals: .row(id: question.id))
                                            .onAppear {
                                                focusedQuestion = .row(id: question.id)
                                                // NEW: Scroll to active question
                                                withAnimation {
                                                    scrollProxy.scrollTo(question.id, anchor: .bottom)
                                                }
                                            }
                                    }
                                    
                                    Button("?") {
                                        processUserGuess()
                                    }
                                    .buttonStyle(.borderedProminent)
                                    .font(.none)
                                case .error:
                                    Text("\(question.productGuess)").foregroundColor(.red)
                                    Spacer()
                                case .right:
                                    Text("\(question.productGuess)").foregroundColor(.green)
                                    Spacer()
                                default:
                                    Text("__")
                                    Spacer()
                                }
                            }
                            .font(.custom("SF Compact", size: 40, relativeTo: .largeTitle))
                            .padding(.vertical)
                        }
                        // new: id for scrollto
                        .id(question.id)
                    }
                }
            }
ChrisR
  • 9,523
  • 1
  • 8
  • 26
  • Thanks a lot for the effort but I used the @Yrb approach. I ended up delaying the scrolling due to the keyboard appearance animation. It looks like the scroll gets the view not yet shrinked by the keyboard in my case. – Gilson Cavalcanti Feb 25 '22 at 02:28