2017-03-25 9 views
0

私は、次のようにテキストをフォーマットするように有効期限フィールドをフォーマットしようとしています:MM/YY ユーザーが入力しているときに追加の文字で追加することができます数字を削除するときには、コードを2文字以上通してから "/"をもう一度付け加えることはできません。ユーザーがテキストフィールドチェックを削除してバイパスしているときに私が認識できる方法はありますか?ユーザーが入力しているときにスウィートフォーマットのテキストフィールド

ExpiryOutlet.addTarget(self, action: #selector(ExpiryDidChange(_:)), for: .editingChanged) 

func ExpiryDidChange(_ textField: UITextField) { 
    if textField == ExpiryOutlet { 

     if textField.text != "" && textField.text?.characters.count == 2 { 
      textField.text = "\(textField.text!)/" 
     } 

    } 
} 

おかげ

+0

"/"を示すラベルを持つ2つの 'UITextField'を常に持ち、同じロジックを使ってfirstResponderのステータスを切り替えることができます – Russell

答えて

0

ユーザーが削除してテキストフィールドチェックをバイパスしたときに私が認識できる方法はありますか?

問題は、間違った方法を実装したことです。デリゲートメソッドtext​Field(_:​should​Change​Characters​In:​replacement​String:​)を実装します。これにより、のテキストが変更されている(2番目のパラメータは範囲です)と、とは何かを区別できます。新しいテキストは - バックスペースの場合はreplacementStringが空になります。

+0

これは、ユーザーがテキストフィールドから数字を削除することを認識して解決しました。 – user7684436

1

あなたはのUITextFieldのサブクラスを作成し、唯一のUIを更新するために、セレクタでeditingChanged controlEventsのためにあなたのオブジェクトにターゲットを追加することにより、入力された番号へのユーザーを許可するカスタムフィールドを作成することができます。

まずサブクラスのUITextFieldをすることができます:

class ExpirationField: UITextField { 
    var allowsExpiredDate = false 
    override func didMoveToSuperview() { 
     super.didMoveToSuperview() 
     placeholder = "MM/YY" 
     addTarget(self, action: #selector(editingChanged), for: .editingChanged) 
     keyboardType = .numberPad 
     textAlignment = .center 
     editingChanged() 
    } 
} 

我々は適切にその文字列表現にflatMapを使ってint型に変換し、範囲内のintの配列を返す以外のすべての数字の文字をフィルタリングすることにより、フィールドのテキストの書式を設定することも必要0から9までです。文字列の桁数を切り替えることにより、ユーザーが入力した桁数に応じてスラッシュ文字を配置する必要があります。有効期限フィールドであることを考慮すると、ユーザーが入力した月と年がまだ有効かどうかを確認する必要があります。 ExpirationFieldに月と年のプロパティを追加して値を返します。我々は有効期限を検証するために、現在の月と年と比較することができますので、同じ日付に適用されます。


extension ExpirationField { 
    var string : String { return text ?? "" } 
    var numbers: [Int] { return string.characters.flatMap{ Int(String($0)) } } 
    var year: Int { return numbers.suffix(2).integer } 
    var month: Int { return numbers.prefix(2).integer } 
    func editingChanged() { 
     text = expirationFormatted 
     if text?.characters.count == 5 { 
      print("Month:", month, "Year:", year, "isValid:", isValid) 
      if !allowsExpiredDate && !isValid { 
       text = numbers.prefix(2).string + "/" + numbers.dropLast().suffix(1).string 
      } 
     } else { 
      print("isValid:", false) 
      switch numbers.count { 
      case 1 where numbers.integer > 1: 
       text = "" 
      case 2 : 
       if numbers.integer > 12 { 
        text = "1" 
       } else if numbers.integer == 0 { 
        text = "0" 
       } 
      case 3 where (numbers.last ?? 0) < 1 && !allowsExpiredDate: 
       text = numbers.dropLast().string 
      case 4 where year + 2000 < Date().year && !allowsExpiredDate: 
       text = numbers.prefix(2).string + "/" + numbers.dropLast().suffix(1).string 
      default: 
       break 
      } 
     } 
     if isValid { 
      layer.borderColor = UIColor.darkGray.cgColor 
      layer.cornerRadius = 3 
      layer.borderWidth = 1 
     } else { 
      layer.borderColor = UIColor.clear.cgColor 
      layer.borderWidth = 0 
     } 
    } 
    var expirationFormatted: String { 
     let numbers = self.numbers.prefix(4) 
     switch numbers.count { 
     case 1...2: return numbers.string 
     case 3: return numbers.prefix(2).string + "/" + numbers.suffix(1).string 
     case 4: return numbers.prefix(2).string + "/" + numbers.suffix(2).string 
     default: return "" 
     } 
    } 
    var isValid: Bool { 
     if string.characters.count < 5 { return false } 
     guard 1...12 ~= month else { 
      print("invalid month:", month) 
      return false 
     } 
     guard Date().year-2000...99 ~= year else { 
      print("invalid year:", year) 
      return false 
     } 
     return year > Date().year-2000 ? true : month >= Date().month 
    } 
    override func deleteBackward() { 
     text = numbers.dropLast().string 
     text = expirationFormatted 
     layer.borderColor = UIColor.clear.cgColor 
     layer.borderWidth = 0 
    } 
} 

extension Calendar { 
    static let iso8601 = Calendar(identifier: .iso8601) 
} 

extension Date { 
    var year: Int { 
     return Calendar.iso8601.component(.year, from: self) 
    } 
    var month: Int { 
     return Calendar.iso8601.component(.month, from: self) 
    } 
} 

extension Collection where Iterator.Element == Int { 
    var string: String { 
     return map(String.init).joined() 
    } 
    var integer: Int { return reduce(0){ 10 * $0 + $1 } } 
} 

その後、あなたは自分のビューにテキストフィールドをドラッグし、それを選択し、インスペクタでExpirationFieldにカスタムクラスを設定します。

enter image description here

Sample

+0

サブクラスのポイントは何ですか? 'editingChanged'はアクションターゲットだけが必要です。 – matt

+0

サブクラス化で何が問題になりますか? –

+0

私は何かが間違っていたとは言いませんでした。しかし、それはまったく必要ではないようです。 – matt

関連する問題