Adding support for avoiding the on-screen keyboard hiding important UI.
Prior to iOS 14, when the on-screen keyboard is displayed it can obscure text fields or other important content. However, with iOS 14 the system now provides automatic built-in support for keyboard avoidance:
When the keyboard activates the screen contents appear to move up. However, what's actually happening is that iOS is compressing any view elements that it can to make space for the keyboard:
struct ContentView: View {
@State private var username = ""
@State private var password = ""
var body: some View {
VStack(alignment: .leading) {
// Introduce a "squishable" view element to show how iOS14 will compress space
// if possible when the keyboard's displayed
GeometryReader { geometry in
Color.red
.overlay(Text("Height = \(Int(geometry.size.height))"))
}
Spacer()
HStack {
Text("Username:").frame(width: 100, height: 35, alignment: .leading)
TextField("enter username", text: $username)
}
HStack {
Text("Password:").frame(width: 100, height: 35, alignment: .leading)
SecureField("enter password", text: $password)
}
HStack {
Button(action: {
print("Username = \(username)")
print("Password = \(password)")
}, label: {
Text("Submit")
.font(.title)
.frame(width: 250, height: 75, alignment: .center)
.background(Color.blue)
.cornerRadius(10)
.foregroundColor(.white)
.padding()
})
}.frame(maxWidth: .infinity)
}
.padding()
}
}If we make the color block of fixed size then iOS moves the view up, pushing the color block off the top of the screen:
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Color.red
.overlay(Text("Height = 610 (fixed)"))
.frame(height: 610) // Fixed height
Another easy way to support keyboard avoidance is to place your content inside a ScrollView, rather than a VStack.
Using this approach iOS automatically scrolls the view correctly to make sure the view with focus is visible:
struct ContentView: View {
var body: some View {
ScrollView { // Use a ScrollView
Color.red
.overlay(Text("Height = 610 (fixed)"))
.frame(height: 610) // FixedIf you want to support keyboard avoidance in iOS 13 then you need to subscribe to the UIApplication.keyboardWillShowNotification and
UIApplication.keyboardWillHideNotification notifcations and adjust the layout of your view accordingly.
The essential idea is to listen for keyboard hide/show notifications and then add/remove padding to your view's container based on the height of the keyboard:
import UIKit
import Combine
class ContentViewModel: ObservableObject {
@Published var keyboardHeight: Int = 0
var showKeyboard: AnyCancellable?
var hideKeyboard: AnyCancellable?
init() {
showKeyboard = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.sink(receiveValue: { notification in
if let userInfo = notification.userInfo,
let kbRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
self.keyboardHeight = Int(kbRect.height)
}
})
hideKeyboard = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.sink(receiveValue: { notification in
self.keyboardHeight = 0
})
}
}
struct ContentView: View {
@StateObject var viewModel = ContentViewModel()
var body: some View {
ScrollView {
Color.red
.overlay(Text("Height = 610 (fixed)\nKeyboard height = \(viewModel.keyboardHeight)"))
.frame(height: 610) // Fixed
Spacer()
}
.padding(.bottom, CGFloat(viewModel.keyboardHeight))
}
}However, it's recommended to use one of the available open source solutions such as:



