LoginSignup
48
32

More than 3 years have passed since last update.

iOS14からテキストフィールドがキーボードに隠れるのを自動調整してくれるようになった

Posted at

はじめに

Xcode12、SwiftUIで動作を確認しました。

iOS14のテキストフィールド

従来のアプリ開発において、キーボード入力のある画面では下記のような挙動が発生してしまうケースが度々発生し、キーボードの高さ分だけ表示位置を調整するといった実装を行うことがよくありました。

  1. TextFieldにフォーカスが当たる
  2. ソフトウェアキーボードが表示される
  3. キーボードが表示されたことでTextFieldが見えなくなってしまう

struct ContentView: View {
    @State private var text: String = ""

    var body: some View {
        VStack {
            Spacer()
            TextField("input here", text: $text)
                .padding()
        }
    }
}

このような状況に対して、iOS14では入力中のTextFieldがキーボードの表示領域外に出てくるように、自動で表示調整がされるようになっています。

これは大変便利な変更ですが、注意が必要な点もあります。

注意点

この自動調整挙動は、iOS14でのみ行われるため、iOS13では表示調整用の実装を従来通り自前で実装する必要があります。

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

しかし、ここでOS間の挙動の違いを意識していないと、以下のようにiOS14で想定していない位置にまでTextFieldが移動してしまうことになります。

そのため、iOS13以下をサポートする場合は、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
                }
            }
        }
    }
}

挙動の違い

最後に、画面構成によってどのような挙動の違いが出るかをまとめておきます。

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: 表示されないので注意!

十分なスペースがある場合

struct ContentView: View {
    @State private var text: String = ""

    var body: some View {
        VStack {
            ForEach(0..<20) {
                Text("\($0)")
            }
            TextField("input here", text: $text)
                .padding()
        }
    }
}

48
32
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
32