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)
}
}
}
}
}
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.
The problem is that when you tap on Field 3 or Field 5, the keyboard appears and pushes the form content upward. Since Field 3 and Field 5 are located at the bottom of the form, they are obscured by the keyboard. There are couple of ways you can use to fix this
Use a ScrollView to wrap your Form and set the .keyboardDismissMode
to .interactive
. This will allow the user to dismiss the keyboard by swiping down on the ScrollView.
ScrollView(.vertical, showsIndicators: false) {
Form {
//...
}
}.keyboardDismissMode(.interactive)
Wrap the Form in a GeometryReader
and use its safeAreaInsets
to adjust the offset of the form when the keyboard appears
var body: some View {
GeometryReader { geometry in
VStack {
Form {
//...
}
}
.offset(y: -geometry.safeAreaInsets.bottom)
}
}
Use the KeyboardResponder
class to listen for keyboard events and adjust the offset of the form when the keyboard appears:
struct TextFieldScrollingIssue: View {
@State private var testInput:String = ""
@ObservedObject var keyboard = KeyboardResponder()
var body: some View {
VStack {
Form {
//...
}
.offset(y: -keyboard.currentHeight)
}
}
}
class KeyboardResponder: ObservableObject {
private var notificationCenter: NotificationCenter
@Published private(set) var currentHeight: CGFloat = 0
init(center: NotificationCenter = .default) {
notificationCenter = center
notificationCenter.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyBoardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
currentHeight = keyboardSize.height
}
}
@objc func keyBoardWillHide(notification: Notification) {
currentHeight = 0
}
}
This issue is likely caused by the fact that the decimalPad keyboard type has a different height than the standard keyboard, and the layout of the view is not adjusting accordingly. One way to fix this issue would be to wrap the TextField views in a VStack and add a padding or offset modifier to the bottom of the VStack to adjust for the difference in keyboard height.
Another solution would be to use a third-party library like IQKeyboardManagerSwift which can automatically handle the keyboard and make sure the text fields are not hidden.
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: ")) {
VStack {
TextField("Field 2", text:$testInput)
TextField("Field 3", text:$testInput)
.keyboardType(.decimalPad)
.offset(y: -100) // add offset
}
}
Section(header: Text("Section 2: ")) {
VStack {
TextField("Field 4", text:$testInput)
TextField("Field 5", text:$testInput)
.keyboardType(.decimalPad)
.offset(y: -100) // add offset
}
}
}
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.