概要
テキストの文字数をリアルタイムでカウントして表示したり、入力文字数の制限をする方法についてCombineフレームワークとonChangeを使用した手法をそれぞれ紹介したいと思います。
環境
この記事は以下のバージョン環境のもと作成されたものです。
【Xcode】14.0.1
【iOS】16.0
【macOS】Monterey バージョン 12.5
Combineでの実装
Combineでの実装方法は以下の通りです。
Combineフレームワークを使用しますので必ずimport Combine
でImportして下さい。
struct HogeView: View { @State private var hogeText = "" @State private var hogeTextCount = 0 let maximumCharacters = 10 var body: some View { VStack(alignment: .leading, spacing: 0) { Text("テキスト入力") .padding(.leading) TextEditor(text: $hogeText) .frame(height: 400) .background { Rectangle() .stroke(lineWidth: 2) } .padding() .onReceive(Just(hogeText)) { inputText in if inputText.count > maximumCharacters { hogeText = String(inputText.prefix(maximumCharacters)) } hogeTextCount = inputText.count <= maximumCharacters ? inputText.count : maximumCharacters } HStack { Spacer() Text("\(hogeTextCount) / \(maximumCharacters)") } .padding(.trailing) } } }
onReceiveを使用して実装します。onReceiveドキュメントに以下の様に記載されています。
このビューが特定のパブリッシャーによって発行されたデータを検出したときに実行するアクションを追加します。
引用元: https://developer.apple.com/documentation/swiftui/grid/onreceive(_:perform:)
onReceiveの引数にJustを使用します。Justは各サブスクライバーに 1 回だけ出力を発行して終了するパブリッシャーなので、hogeTextが変更される度に一回だけクロージャー内の処理が実行されます。
後は入力したテキストに対して定義した最大の文字を超えるとprefixによって取得した最大の文字数を代入して、テキストの表示制御を行なっています。
文字数のカウントに関してはinputText.count <= maximumCharacters ? inputText.count : maximumCharacters
の部分で入力したテキストの文字数を取得して表示しています。
表示するだけだと上記で定義した最大の文字数を超えてしまうので、超えないように三項演算子で処理を行なっています。
ただViewModelの様なclassでプロパティやメソッドを管理すると取り回しが面倒になります。
onChangeでの実装
onChangeでの実装もほぼonReceiveでの実装と変わりないです。
onReceiveの部分を以下に置き換えるだけです。
.onChange(of: hogeText) { inputText in if inputText.count > maximumCharacters { hogeText = String(inputText.prefix(maximumCharacters)) } hogeTextCount = inputText.count <= maximumCharacters ? inputText.count : maximumCharacters }
onChangeは以下の通りです。
特定の値が変更されたときにアクションを起動する、このビューの修飾子を追加します。
引用元: https://developer.apple.com/documentation/swiftui/view/onchange(of:perform:)
使い方はシンプルでonChange(of: xxx)のxxxの部分を監視して、変更されるとクロージャー内の処理を実行します。
onReceiveより分かりやすいです。
またclassでプロパティやメソッドを管理する際も簡単に行えます。
class HogeViewModel: ObservableObject { @Published var hogeText = "" @Published private(set) var hogeTextCount = 0 let maximumCharacters = 10 func hogeTextCountConverter(inputText: String) { hogeText = hogeTextUpperLimitConverter(inputText: inputText) hogeTextCount = hogeTextCountUpdater(inputText: inputText) } private func hogeTextUpperLimitConverter(inputText: String) -> String { if inputText.count > maximumCharacters { return String(inputText.prefix(maximumCharacters)) } else { return inputText } } private func hogeTextCountUpdater(inputText: String) -> Int { return inputText.count <= maximumCharacters ? inputText.count : maximumCharacters } }
struct HogeView: View { @StateObject private var viewModel = HogeViewModel() var body: some View { VStack(alignment: .leading, spacing: 0) { Text("テキスト入力") .padding(.leading) TextEditor(text: $viewModel.hogeText) .frame(height: 400) .background { Rectangle() .stroke(lineWidth: 2) } .padding() .onChange(of: viewModel.hogeText) { inputText in viewModel.hogeTextCountConverter(inputText: inputText) } HStack { Spacer() Text("\(viewModel.hogeTextCount) / \(viewModel.maximumCharacters)") } .padding(.trailing) } } }
まとめ
時と場合によって使い分けていただければと思います。ただSwiftUIで実装するならonChangeを使用した方が圧倒的に楽な気がします。
また記入したかった文字は「今日もいい天気で気持ちがいい」だったので入力できる文字数を増やしたいと思います。