'Is there a SwiftUI "pure" way of auto scroll a List to show a TextField consistently? (keyboard is hiding the field)
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:
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. 🤗
Solution 1:[1]
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)
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ChrisR |

