[SwiftUI] TextField の onEditingChanged と onCommit が deprecated になる件

SwiftUI2021

     

TAGS:

⌛️ 2 min.
TextField の onEditingChanged と onCommit が deprecated になるようなので、代替の方法をまとめます。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterey 12.5 Beta
  • Xcode 14.0 Beta2
  • iOS 16.0 beta

TextField

SwiftUI でユーザーからの(主として Text)入力を受ける ビューに TextField があります。

この TextField の挙動を指定する オプションに、 onEditingChanged と onCommit 指定があります。
これらが iOS16/macOS13 で deprecated になり、別の書き方が必要となりますので、方法をまとめてみます。

TextField の onEditingChanged

TextField の初期化時に、onEditingChanged 引数に、closure を与えることができました。

TextField が編集中かどうか 変化した時に onEditingChanged が呼び出されます。
呼び出される時には、現在 編集中かどうかの フラグが渡されてきます。

そのフラグを使用することで、編集開始前/編集終了時に行いたい処理を行うことができました。

MEMO

編集開始は、iOS と macOS でタイミングが異なります。
iOS では、TextField にフォーカスが与えられた時に呼び出されます。
macOS では、フォーカスが与えられただけでは呼び出されず、キー入力/削除 等の編集が開始された時に呼び出されます。(iOS16 Beta, macOS 12.5 Beta で確認)
編集終了とは、iOS/macOS 共に、TextField がフォーカスを失うことを意味します。(リターンキー押下では呼ばれません)

TextField の onCommit

TextField の初期化時に、onCommit 引数として 別の closure を渡すこともできます。

この onCommit は、ユーザーが リターンキーを押下したときに 呼び出されます。

使用例

以下は、onEditingChanged と onCommit を使用した簡単なサンプルです。

ExampleOnMac

# 上記は、macOS 上のスナップショットですが、iOS でも動作します。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2022/06/24
//  © 2022  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
    @State private var str = "Hello"
    @State private var world = "world"
    var body: some View {
        VStack {
            TextField("TextField", text: $str, onEditingChanged: { editing in
                if editing {
                    print("started to edit in textField")
                } else {
                    print("finished textField editing")
                }
            }, onCommit: {
                print("onCommit")
            }).padding()
            TextField("Dummy", text: $world).padding() // フォーカス移動のため
        }
    }
}

上記のコードを実行することで、onEditingChanged/onCommit がそれぞれ どのようなタイミングで呼ばれるかを確認できます。

# 2 つめの TextField は、観察対象の TextField のフォーカスを明示的に外せるように配置しています。

onEditingChanged/onCommit を置き換える

deprecated になる onEditingChagned と onCommit を置き換えていきます。

onEditingChange の代替

Apple のドキュメントによると、onEditingChanged の代わりに、FocusState と .focused を使いなさいと書いてあります。

Apple のドキュメントは、こちら

先に挙げた 動作確認の例を書き換えていきます。

なお、iOS と macOS では onEditingChanged の振る舞いは異なりますが、iOS での onEditingChanged の振る舞いと同じになるように書いていきます。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2022/06/24
//  © 2022  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
    @State private var str = "Hello"
    @State private var world = "world"
    @FocusState var textFieldFocus: FocusedField?
    enum FocusedField {
        case Target
        case Dummy
    }
    var body: some View {
        VStack {
            TextField("TextField", text: $str, onCommit: {
                print("onCommit")
            }).padding()
                .focused($textFieldFocus, equals: .Target)
                .onChange(of: textFieldFocus, perform: { newValue in
                    if newValue == .Target {
                        print("TextField got focus")
                        print("i.e. same with onEditingChanged with true")
                    } else {
                        print("TextField loose focus")
                        print("i.e. same with onEditingChanged with false")
                    }
                })
            TextField("Dummy", text: $world).padding() // フォーカス移動のため
        }
    }
}

FocusState/.focused/.onChange を組み合わせて onEditingChanged と同等の振る舞いを実現する実装の内訳は以下の通りです。

・FocusState を使って、変数 textFieldFocus: FocusedField を定義し、現在フォーカスされているビューを確認できるようにします。
・TextField には、.focused を付与し、フォーカスされているかどうかを textFieldFocus に同期します。
・textFieldFocus の変化を .onChange を使用して監視し、変化があった時に処理を実行します。

MEMO

FocusState は、複数のビューのフォーカスを管理することもできるため、複数のビューのフォーカス変化について監視しようとすると、.onChange が複雑になっていきます。

onCommit の代替

Apple のドキュメントによると、onCommit の代わりに、onSubmit を使いなさいと書いてあります。

Apple のドキュメントは、こちら

先の例を書き換えていきます。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2022/06/24
//  © 2022  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
    @State private var str = "Hello"
    @State private var world = "world"
    @FocusState var textFieldFocus: FocusedField?
    enum FocusedField {
        case Target
        case Dummy
    }
    var body: some View {
        VStack {
            TextField("TextField", text: $str)
                .padding()
                .focused($textFieldFocus, equals: .Target)
                .onChange(of: textFieldFocus, perform: { newValue in
                    if newValue == .Target {
                        print("TextField got focus")
                        print("i.e. same with onEditingChanged with true")
                    } else {
                        print("TextField loose focus")
                        print("i.e. same with onEditingChanged with false")
                    }
                })
                .onSubmit {
                    print("onCommit here")
                }
            TextField("Dummy", text: $world).padding() // フォーカス移動のため
        }
    }
}

onCommit の置き換えは、非常に簡単です。

onCommit の処理を View Modifier である .onSubmit に入れ替えると完了です。

まとめ

iOS16/macOS13 で deprecated になる TextField の onEditingChanged と onCommit の置き換えについてまとめました。

TextField の onEditingChanged と onCommit の置き換え
  • onCommit は、.onSubmit に置き換える
  • onEditingChanged は、FocusState/.focused/.onChange を使用して置き換える
  • onEditingChanged の振る舞いは、iOS と macOS で異なるので注意する

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

SwiftUI おすすめ本

SwiftUI を理解するには、以下の本がおすすめです。

# 少し古くなってしまいましたが、基本的なところを理解できる 良書だと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です