SwiftUI
TextField
Keyboard
iOS Development
UI Enhancement

Move TextField up when the keyboard has appeared in SwiftUI

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In SwiftUI, the keyboard can easily cover a TextField if your layout is fixed near the bottom of the screen. Newer SwiftUI containers handle some keyboard avoidance automatically, but custom forms, overlays, and bottom-aligned layouts often still need explicit adjustment. This article shows a practical keyboard-aware approach and explains when simpler layout choices are enough.

Start with the Simplest Layout

Before adding keyboard observers, check whether a ScrollView or Form solves the problem by itself. Those containers already cooperate better with the system keyboard than a rigid VStack.

swift
1import SwiftUI
2
3struct SimpleFormView: View {
4    @State private var name = ""
5    @State private var email = ""
6
7    var body: some View {
8        Form {
9            TextField("Name", text: $name)
10            TextField("Email", text: $email)
11                .keyboardType(.emailAddress)
12                .textInputAutocapitalization(.never)
13        }
14        .navigationTitle("Profile")
15    }
16}

If this layout already keeps fields visible, do not add custom keyboard code. Extra keyboard handling adds maintenance cost.

When You Need a Keyboard-Aware Offset

If the screen uses a bottom action bar or a custom stack, a keyboard height observer is usually the cleanest fix. The basic idea is:

  • watch keyboard show and hide notifications
  • store the keyboard height in state
  • add bottom padding equal to that height

The observer below exposes the visible keyboard height in SwiftUI-friendly form.

swift
1import SwiftUI
2import Combine
3
4final class KeyboardObserver: ObservableObject {
5    @Published var height: CGFloat = 0
6
7    private var cancellables = Set<AnyCancellable>()
8
9    init() {
10        let willShow = NotificationCenter.default.publisher(
11            for: UIResponder.keyboardWillShowNotification
12        )
13        let willHide = NotificationCenter.default.publisher(
14            for: UIResponder.keyboardWillHideNotification
15        )
16
17        willShow
18            .compactMap { notification -> CGFloat? in
19                let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
20                return frame?.height
21            }
22            .merge(with: willHide.map { _ in 0 })
23            .receive(on: RunLoop.main)
24            .assign(to: &$height)
25    }
26}

This keeps UIKit keyboard details outside the view itself.

Build a Keyboard-Aware View

Apply the observer's value as bottom padding to shift the input region upward.

swift
1import SwiftUI
2
3struct LoginView: View {
4    @StateObject private var keyboard = KeyboardObserver()
5    @State private var email = ""
6    @State private var password = ""
7
8    var body: some View {
9        VStack(spacing: 16) {
10            Spacer()
11
12            Text("Sign In")
13                .font(.largeTitle.bold())
14
15            TextField("Email", text: $email)
16                .textFieldStyle(.roundedBorder)
17                .keyboardType(.emailAddress)
18                .textInputAutocapitalization(.never)
19
20            SecureField("Password", text: $password)
21                .textFieldStyle(.roundedBorder)
22
23            Button("Continue") {
24                print("submit")
25            }
26            .buttonStyle(.borderedProminent)
27
28            Spacer()
29        }
30        .padding()
31        .padding(.bottom, keyboard.height)
32        .animation(.easeOut(duration: 0.25), value: keyboard.height)
33    }
34}

When the keyboard appears, the extra bottom padding moves the content upward and keeps the focused field visible.

Account for Safe Area and Home Indicator

Keyboard height includes the portion overlapping the screen, but your layout may also have bottom safe area insets. On some devices, applying full height can push content too far upward.

You can subtract the bottom inset when needed:

swift
1struct KeyboardAwareContainer<Content: View>: View {
2    @StateObject private var keyboard = KeyboardObserver()
3    let content: () -> Content
4
5    var body: some View {
6        GeometryReader { proxy in
7            content()
8                .padding(.bottom, max(0, keyboard.height - proxy.safeAreaInsets.bottom))
9                .animation(.easeOut(duration: 0.25), value: keyboard.height)
10        }
11    }
12}

This usually produces more natural spacing on modern iPhones.

Dismissing the Keyboard

Moving the field up is only part of the experience. Users also need a clean way to dismiss the keyboard when they finish typing.

swift
1import SwiftUI
2
3extension View {
4    func hideKeyboard() {
5        UIApplication.shared.sendAction(
6            #selector(UIResponder.resignFirstResponder),
7            to: nil,
8            from: nil,
9            for: nil
10        )
11    }
12}

Then attach a tap gesture to the background if appropriate:

swift
1.contentShape(Rectangle())
2.onTapGesture {
3    hideKeyboard()
4}

Use this carefully so it does not interfere with other gestures.

Prefer ScrollView for Larger Forms

If the screen has many fields, offsetting the entire view can become awkward. A ScrollView with focus management is often better because users can continue navigating without the whole interface jumping abruptly.

That pattern is especially useful for registration flows, profile editors, and checkout screens.

Common Pitfalls

  • Adding keyboard observers when Form or ScrollView would already solve the issue.
  • Applying raw keyboard height without considering bottom safe area inset.
  • Moving the whole screen when only the input region needs adjustment.
  • Forgetting to animate the layout change, which makes the jump feel abrupt.
  • Keeping custom keyboard code in many views instead of extracting a reusable observer.

Summary

  • Start with Form or ScrollView before building custom keyboard avoidance.
  • For custom layouts, observe keyboard notifications and add bottom padding.
  • Subtract safe area inset when full keyboard height pushes content too far.
  • Add a keyboard dismissal path so the form feels complete.
  • Reuse a small KeyboardObserver helper instead of repeating UIKit glue code.

Course illustration
Course illustration

All Rights Reserved.