あなたはの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にカスタムクラスを設定します。
Sample
"/"を示すラベルを持つ2つの 'UITextField'を常に持ち、同じロジックを使ってfirstResponderのステータスを切り替えることができます – Russell