【Xcode/Swift】RxSwiftを学ぶ (TextField編)

テキストフィールドの入力に応じてリアルタイムにLabelに表示させる、入力できる文字タイプに制限をかける、同じ文字の入力が◯文字以上続いた時に警告Labelを出す等々を実装してみました。参考に出来る部分があれば幸いです。


Storyboard

◎Main.storyboard

◎NextViewController.storyboard

NextViewControllerはめんどくさかったら作らなくてもOKです、ただ画面遷移処理部分が出来なくなるだけなので。


コード記述

RxSwiftをインストールしている前提なので先にインストールを忘れずに、、、

pod 'RxSwift', '6.5.0'
pod 'RxCocoa', '6.5.0'
import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak private var inputUserIDTextField: UITextField!
    @IBOutlet weak private var mistakeUserIDLabel: UILabel!
    @IBOutlet weak private var countUserIDCharactersLabel: UILabel!
    @IBOutlet weak private var inputPasswordTextField: UITextField!
    @IBOutlet weak private var mistakePasswordLabel: UILabel!
    @IBOutlet weak private var signUpButton: UIButton! {
        didSet {
            signUpButton.layer.cornerRadius = 15.0 // ボタンを角丸にする
        }
    }

    private let maxTextLength: Int = 10 // 最大入力文字数
    private let minimumTextLength: Int = 6 // 最低入力文字数
    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        configureInputUserIDTextField()
        configurePasswordInputTextField()
        configureSignUpButton()
    }

    private func configureInputUserIDTextField() {
        inputUserIDTextField.rx.text.subscribe(onNext: { text in
            if let text = text, text.count >= self.maxTextLength {
                self.inputUserIDTextField.text = text.prefix(self.maxTextLength).description
            }
            let inputTextLength = self.inputUserIDTextField.text?.count ?? 0
            let remainCount = self.maxTextLength - inputTextLength // 残り入力可能文字数
            self.countUserIDCharactersLabel.text = "残り\(remainCount)文字"
            self.signUpButton.isEnabled = (inputTextLength > 0)
            guard let isMistakUserIDLabel = text?.isAlphanumeric() else { return }
            if inputTextLength != 0 { // 文字の使用可否を判定する
                self.mistakeUserIDLabel.isHidden = isMistakUserIDLabel
                self.mistakeUserIDLabel.text = "使用できない文字が含まれています"
            } else {
                self.mistakeUserIDLabel.isHidden = true
            }

        }).disposed(by: disposeBag)
    }

    private func configurePasswordInputTextField() {
        inputPasswordTextField.rx.text.subscribe(onNext: { text in
            let inputTextLength = self.inputPasswordTextField.text?.count ?? 0
            guard let isMistakePasswordLabal = text?.isCharacterString(text: text!) else { return }
            if inputTextLength != 0 { // 3文字以上同じなら警告を出す
                self.mistakePasswordLabel.isHidden = !isMistakePasswordLabal
                self.mistakePasswordLabel.text = "予測されやすいパスワードです"
            } else {
                self.mistakePasswordLabel.isHidden = true
            }
            // 両方のTextFieldに入力がされていたら画面遷移を可能にする
            self.signUpButton.isEnabled = (inputTextLength >= self.minimumTextLength) && self.inputUserIDTextField.text != ""
        }).disposed(by: disposeBag)

    }

    private func configureSignUpButton() {
        signUpButton.rx.tap.subscribe(onNext: {
            let storyboard = UIStoryboard(name: "NextViewController", bundle: nil)
            guard let nextViewController = storyboard.instantiateViewController(withIdentifier: "NextViewController") as? NextViewController else {
                fatalError("遷移先がありません")
            }
            self.present(nextViewController, animated: true)
        }).disposed(by: disposeBag)
    }

}

extension String {
    // 半角英数字を判定する関数
    func isAlphanumeric() -> Bool {
        return NSPredicate(format: "SELF MATCHES %@", "[a-zA-Z0-9]+").evaluate(with: self)
    }

    // 同じ文字が3文字以上連続しているかの判定をする関数
    func isCharacterString(text: String) -> Bool {
        if text.isEmpty { return false }
        var sameCharacterCount = 0
        var characterArray: [Character] = []
        for index in text.description {
            characterArray.append(index)
        }
        for i in 1 ..< text.count {
            if characterArray[i - 1] == characterArray[i] {
                sameCharacterCount += 1
            } else {
                sameCharacterCount = 0
            }
        }
        return sameCharacterCount >= 2
    }

}

まとめ

結論: RxSwiftは難しい


◎参考文献