LoginSignup
2
6

More than 5 years have passed since last update.

[Swift3] 整数入力用UITextFieldを自作してみた

Last updated at Posted at 2017-02-12

イメージ

NumberTextField.gif

仕様

  • 整数値専用のUITextField
  • 文字は入力不可 ※keyboardTypeにnumberPadを指定するが、コピペ対策
  • 編集終了時にカンマ編集を行い、フォーカスインでカンマを削除する
  • カンマ編集する/しないはIB上でプロパティ指定可能

環境

Items Version
Xcode 8.2
Swift 3.0.2

コード

定義

NumberTextField.swift
import UIKit

@IBDesignable

/// 数値専用UITextField
class NumberTextField: UITextField {

    // MARK: - 変数

    /// カンマ編集有無
    private var isCommaFormat = true

    /// カンマ編集有無
    @IBInspectable var CommaFormat: Bool = true {
        didSet {
            self.isCommaFormat = CommaFormat
        }
    }

    // MARK: - Controll Event

    /// ロードされた後に呼び出される
    override func awakeFromNib() {
        super.awakeFromNib()
        self.keyboardType = .numberPad
        self.textAlignment = .right
    }

    // MARK: - Method

    /// 入力が開始された際に呼ばれる
    func beginEditing() {
        guard let text = self.text else {
            return
        }
        self.text = text.replacingOccurrences(of: ",", with: "")
    }

    /// 入力値が変更された場合に呼ばれる
    func shouldChange(range: NSRange, string: String) -> Bool {
        // 0-9以外の数字以外不可
        return string.isEmpty || string.range(of: "^[0-9]+$", options: .regularExpression) != nil
    }

    /// 入力値が確定された場合に呼ばれる
    func endEditing() {
        guard let text = self.text else {
            return
        }
        if self.isCommaFormat {
            self.text = Int(text)?.withComma
        }
    }

}
Extentions.swift
import Foundation

/// Int拡張
extension Int {
    /// カンマ付け
    var withComma: String {
        let decimalFormatter = DecimalFormatter()
        guard let s = decimalFormatter.string(from: self as NSNumber) else {
            fatalError()
        }
        return s
    }
}

/// String拡張
extension String {
    /// カンマ区切り数字のカンマどり
    var noComma: Int {
        if self.characters.count == 0 {
            return 0
        }
        let decimalFormatter = DecimalFormatter()
        guard let i = decimalFormatter.number(from: self) else {
            preconditionFailure("NumberFormatter.number method failure!")
        }
        return Int(i)
    }
}

/// カンマ編集制御用NumberFormatter拡張クラス
class DecimalFormatter: NumberFormatter {
    override init() {
        super.init()
        self.locale = Locale(identifier: "ja_JP")
        self.numberStyle = .decimal
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

使用例

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var numberTextField: NumberTextField!
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.numberTextField.delegate = self
        self.textField.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - UITextField Delegate

    /// UITextFieldの編集開始
    func textFieldDidBeginEditing(_ textField: UITextField) {
        if let numberTextField = textField as? NumberTextField {
            numberTextField.beginEditing()
        }
    }

    /// UITextFieldの値変更
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if let numberTextField = textField as? NumberTextField {
            return numberTextField.shouldChange(range: range, string: string)
        }
        return true
    }

    /// UITextFieldの編集終了
    func textFieldDidEndEditing(_ textField: UITextField) {
        if let numberTextField = textField as? NumberTextField {
            numberTextField.endEditing()
        }
    }

}

課題

ViewControllerに処理をdelegateしておきながら、NumberTextField自身のメソッドを呼ばせるのが何か気持ち悪い。
エレガントな解決方法があったらコメントをお願いします!

2
6
0

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
2
6