I confirmed the operation with Xcode12 and SwiftUI.
In conventional application development, the following behavior often occurs on screens with keyboard input, and it was common to implement such as adjusting the display position by the height of the keyboard.
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
Spacer()
TextField("input here", text: $text)
.padding()
}
}
}
In response to this situation, iOS 14 automatically adjusts the display so that the TextField you are typing appears outside the display area of the keyboard.
This is a very useful change, but there are some things to keep in mind.
Since this automatic adjustment behavior is performed only in iOS14, it is necessary to implement the display adjustment implementation by yourself in iOS13 as before.
struct ContentView: View {
@State private var text: String = ""
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
Spacer()
TextField("input here", text: $text)
.padding()
.padding(.bottom, viewModel.bottomPadding)
}
}
}
class ViewModel: ObservableObject {
@Published var bottomPadding: CGFloat = 0.0
init() {
NotificationCenter.default.addObserver(
forName: UIResponder.keyboardWillChangeFrameNotification,
object: nil,
queue: OperationQueue.main
) { [weak self](notification: Notification) -> Void in
guard let userInfo = notification.userInfo,
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
withAnimation {
self?.bottomPadding = keyboardFrame.size.height
}
}
}
}
However, if you are not aware of the difference in behavior between OSs here, the TextField will move to a position not expected in iOS 14 as shown below.
Therefore, when supporting iOS 13 or lower, it seems necessary to implement to switch the behavior for each OS.
class ViewModel: ObservableObject {
@Published var bottomPadding: CGFloat = 0.0
init() {
if #available(iOS 14, *) {
} else {
NotificationCenter.default.addObserver(
forName: UIResponder.keyboardWillChangeFrameNotification,
object: nil,
queue: OperationQueue.main
) { [weak self](notification: Notification) -> Void in
guard let userInfo = notification.userInfo,
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
withAnimation {
self?.bottomPadding = keyboardFrame.size.height
}
}
}
}
}
Finally, I will summarize what kind of behavior differs depending on the screen configuration.
List
struct ContentView: View {
@State private var text: String = ""
var body: some View {
List {
ForEach(0..<30) {
Text("\($0)")
}
TextField("input here", text: $text)
.padding()
}
}
}
ScrollView
struct ContentView: View {
@State private var text: String = ""
var body: some View {
ScrollView {
ForEach(0..<30) {
Text("\($0)")
}
TextField("input here", text: $text)
.padding()
}
}
}
VStack
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
ForEach(0..<30) {
Text("\($0)")
}
TextField("input here", text: $text)
.padding()
}
}
}
: warning: ** Note that it will not be displayed! ** **
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
ForEach(0..<20) {
Text("\($0)")
}
TextField("input here", text: $text)
.padding()
}
}
}
Recommended Posts