[SWIFT] Starting with iOS 14, text fields are automatically adjusted to be hidden on the keyboard.

Introduction

I confirmed the operation with Xcode12 and SwiftUI.

iOS14 text field

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.

  1. The Text Field is in focus
  2. The software keyboard is displayed
  3. TextField disappears because the keyboard is displayed
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.

important point

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
                }
            }
        }
    }
}

Difference in behavior

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

If you don't have enough space

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! ** **

If there is enough space

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

Starting with iOS 14, text fields are automatically adjusted to be hidden on the keyboard.
How to solve the problem that notification cannot be requested on iOS14
How to adjustTextPosition with iOS Keyboard Extension
Are you still exhausted with CollectionView? CompositionalLayout starting from iOS12 ~ From implementation to design ~
[Swift 5] Processing to close the keyboard on UITableView
[Ruby On Rails] How to use simple_format to display the entered text with line breaks