【SwiftUI】TextFieldの文字数を制限したりスペースを入力させないようにする

2022.02.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

TextFieldで入力フォームを作る際に、文字数を制限したり、スペースを入力してほしくない時がいつかあると思います。そんな時に備えて調べてみました。

環境

  • Xcode 13.2.1

Combineを使う

今回はiOS 13以降で使える非同期フレームワークのCombineを使用します。

import Combine

TextFieldの準備

今回はパスワード入力フォームという前提で進めていきます。入力出来る最大文字数の定数maxPasswordLengthも準備しておきました。

import SwiftUI
import Combine

struct ContentView: View {

    @State private var passwordText = ""
    private let maxPasswordLength = 4

    var body: some View {

        TextField("パスワード", text: $passwordText)
            .padding()
            .border(.black)
            .padding()
    }
}

入力文字のイベントを購読する

TextFieldの入力イベントを購読する為に、onRecieveメソッドを使って、パスワードの入力文字を変更を検知できるようにします。

onReceive(_:perform:)

第一引数には、Publisherを渡して、そのPublisherからのイベント発行がある度に処理を実行してくれます。

func onReceive<P>(_ publisher: P, perform action: @escaping (P.Output) -> Void) -> some View where P : Publisher, P.Failure == Never

onReceiveでイベント発行を受け取るには、passwordTextPublisherに変更してあげる必要がある為、Justを使用して変更します。

Just

JustCombineフレームワークの構造体で、Subscriber(今回でいうonRecieve)に出力を送信出来るPublisherです。

これでpasswordTextの出力がある度に何らかの処理を実行出来るようになりました。

TextField("パスワード", text: $passwordText)
    .padding()
    .border(.black)
    .padding()
    .onReceive(Just(passwordText)) { _ in
        // 実行したい処理
    }

_の箇所は、出力結果として、今回はpasswordTextの値が入ってくるのですが、今回はインスタンスプロパティのpasswordTextの値と変わらない為、使用しない方向で対応しました。

最大文字数を超えた文字を切り捨てる

passwordTextからイベント発行出来るようになったので、後は最大文字数を超えた文字を切り捨てるだけです。

.onReceive(Just(passwordText)) { _ in
        if passwordText.count > maxPasswordLength {
            passwordText = String(passwordText.prefix(maxPasswordLength))
        }
    }

passwordTextの文字数がmaxPasswordLengthを超える場合は、それ以降の文字を切り捨てたものをpasswordTextに代入しています。

スペースを削除する

半角と全角のスペースを文字列から取り除くextensionメソッドを作ってみました。

extension String {

    func removingWhiteSpace() -> String {
        let whiteSpaces: CharacterSet = [" ", " "]
        return self.trimmingCharacters(in: whiteSpaces)
    }
}

removingWhiteSpaceメソッドを使用して、文字列から全角半角スペースを取り除いた値を取得します。

passwordText = passwordText.removingWhiteSpace()

出来たもの

デモ

password

コード

import SwiftUI
import Combine

struct ContentView: View {

    @State private var passwordText = ""
    private let maxPasswordLength = 4

    var body: some View {

        TextField("パスワード", text: $passwordText)
            .padding()
            .border(.black)
            .padding()
            .onReceive(Just(passwordText)) { _ in
                if passwordText.count > maxPasswordLength {
                    // 最大文字数超えた場合は切り捨て
                    passwordText = String(passwordText.prefix(maxPasswordLength))
                }
                // 文字列から全角半角スペースを取り除く
                passwordText = passwordText.removingWhiteSpace()
            }
    }
}

おわりに

本来のパスワードの場合は、最大文字数がもっと多かったり、もっと複雑な入力文字規制があると思います。この方法を応用すると出来そうですね。

何はともあれ、安全なパスワードを運用を心がけましょう。

参考