@@ -16,29 +16,46 @@ protocol InputableField {
1616 func isEmpty( ) -> Bool
1717}
1818
19- class CodeInputField : UIView , InputableField {
20- private var value : String ?
19+ class CodeInputView < T: InputableField & UIView > : UIView , UIKeyInput {
20+ private var fields = [ T] ( )
21+ private var currentIndex = 0
22+ var numberOfFields = 4 {
23+ didSet {
24+ configure ( )
25+ layoutSubviews ( )
26+ }
27+ }
2128
22- func deleteValue( ) {
23- value = " "
29+ var code = " "
30+ var horizontalMargins : CGFloat = 8.0 {
31+ didSet {
32+ layoutSubviews ( )
33+ }
2434 }
2535
26- func setValue( _ newValue: String ) {
27- value = newValue
36+ var interFieldSpacing : CGFloat = 8.0 {
37+ didSet {
38+ layoutSubviews ( )
39+ }
2840 }
2941
30- func getValue( ) -> String {
31- return value ?? " "
42+ var keyboardType : UIKeyboardType = . numberPad
43+
44+ private var sizeOfField : CGFloat {
45+ let frameWidth = frame. width
46+ let minBySides = min ( frameWidth - horizontalMargins * 2 , frame. height * 0.8 )
47+ let fieldsCount = fields. count
48+
49+ return min ( minBySides, ( frameWidth - interFieldSpacing * CGFloat( fieldsCount - 1 ) - horizontalMargins * 2.0 ) / CGFloat( fieldsCount) )
3250 }
3351
34- func isEmpty( ) -> Bool {
35- return value == nil
52+ override var canBecomeFirstResponder : Bool {
53+ return true
54+ }
55+
56+ var hasText : Bool {
57+ return fields. filter ( { !$0. isEmpty ( ) } ) . count > 0
3658 }
37- }
38-
39- class CodeInputView < T: InputableField & UIView > : UIView {
40- var fields = [ T] ( )
41- var numberOfFields = 4
4259
4360 override func awakeFromNib( ) {
4461 super. awakeFromNib ( )
@@ -67,25 +84,58 @@ class CodeInputView<T: InputableField&UIView>: UIView {
6784 private func configure( ) {
6885 guard fields. count != numberOfFields else { return }
6986
70- for _ in 0 ..< numberOfFields {
71- fields. append ( T ( frame: . zero) )
87+ if numberOfFields > fields. count {
88+ ( 0 ..< ( numberOfFields - fields. count) ) . forEach ( { _ in
89+ insertNewField ( )
90+ } )
91+ } else {
92+ fields [ numberOfFields..< fields. count] . forEach ( { $0. removeFromSuperview ( ) } )
93+ fields. removeSubrange ( numberOfFields..< fields. count)
7294 }
7395 }
7496
75- private func layoutFields( ) {
76-
97+ private func insertNewField( ) {
98+ let field = T ( frame: fields. last? . frame ?? . zero)
99+ addSubview ( field)
100+ fields. append ( field)
77101 }
78102
79- var hasText : Bool {
80- return false
103+ private func layoutFields( ) {
104+ let numberOfFields = fields. count
105+ let widthOfFields = CGFloat ( numberOfFields) / 2.0 * sizeOfField
106+ let widthOfInterFieldSpacing = ( CGFloat ( numberOfFields) / 2.0 - 0.5 ) * interFieldSpacing
107+ var x = bounds. midX - ( widthOfFields + widthOfInterFieldSpacing)
108+ let y = ( bounds. height - sizeOfField) * 0.5
109+
110+ fields. forEach ( {
111+ let frame = CGRect ( x: x, y: y, width: sizeOfField, height: sizeOfField)
112+ $0. frame = frame
113+ x += sizeOfField + interFieldSpacing
114+ } )
81115 }
82116
83117 func insertText( _ text: String ) {
118+ guard currentIndex < fields. count else {
119+ return
120+ }
84121
122+ fields [ currentIndex] . setValue ( text)
123+ currentIndex += 1
124+ updateCode ( )
85125 }
86126
87127 func deleteBackward( ) {
128+ guard currentIndex > 0 else {
129+ return
130+ }
88131
132+ currentIndex -= 1
133+ fields [ currentIndex] . deleteValue ( )
134+ updateCode ( )
135+ }
136+
137+ private func updateCode( ) {
138+ code = fields. reduce ( " " , { $0 + $1. getValue ( ) } )
89139 }
90140}
91141
0 commit comments