Skip to content

Commit f00a7d9

Browse files
committed
Add use TextFromatter in FormatterTextField
1 parent cbea112 commit f00a7d9

File tree

1 file changed

+61
-140
lines changed

1 file changed

+61
-140
lines changed

FormattedTextField/FormattedTextField.swift

Lines changed: 61 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import UIKit
1313
}
1414

1515
open class FormattedTextField: UITextField {
16-
open static let maskSymbol: Character = "×"
1716

1817
public override init(frame: CGRect) {
1918
placeholderLabel = UILabel()
@@ -34,8 +33,11 @@ open class FormattedTextField: UITextField {
3433
super.delegate = delegateProxy
3534

3635
if let unformattedText = unformattedText {
37-
var cursorPosition = 0
38-
text = formattedText(fromText: unformattedText, textMask: text, cursorPosition: &cursorPosition)
36+
if let formatter = textFormatter {
37+
text = formatter.formattedText(from: unformattedText)
38+
} else {
39+
text = unformattedText
40+
}
3941
}
4042

4143
placeholderLabel.font = font
@@ -48,22 +50,26 @@ open class FormattedTextField: UITextField {
4850
addSubview(placeholderLabel)
4951
}
5052

51-
@IBInspectable open var textMask: String? {
52-
didSet(oldMask) {
53-
var cursorPosition: Int = 0
54-
if let selectedRange = selectedCharactersRange, let text = text {
55-
cursorPosition = text.distance(from: text.startIndex, to: selectedRange.lowerBound)
56-
} else {
57-
cursorPosition = 0
53+
@IBInspectable open var textFormatter: TextFromatter? {
54+
didSet(oldFormatter) {
55+
let text = (self.text ?? "")
56+
let selectedRange = selectedCharactersRange ?? text.startIndex..<text.startIndex
57+
58+
var unformattedText = text
59+
var unformattedRange = selectedRange
60+
if let oldFormatter = oldFormatter {
61+
(unformattedText, unformattedRange) = oldFormatter.unformattedText(from: text, range: selectedRange)
5862
}
5963

60-
let unformattedText = unformatterText(fromText: (text ?? ""), textMask: oldMask, cursorPosition: &cursorPosition)
61-
let newFormattedText = formattedText(fromText: unformattedText, textMask: textMask, cursorPosition: &cursorPosition)
62-
text = newFormattedText
64+
var formattedText = unformattedText
65+
var formattedRange = unformattedRange
66+
if let formatter = textFormatter {
67+
(formattedText, formattedRange) = formatter.formattedText(from: unformattedText, range: unformattedRange)
68+
}
6369

70+
self.text = formattedText
6471
if selectedTextRange != nil {
65-
let cursorIndex = newFormattedText.index(newFormattedText.startIndex, offsetBy: cursorPosition, limitedBy: newFormattedText.endIndex) ?? newFormattedText.endIndex
66-
selectedCharactersRange = cursorIndex..<cursorIndex
72+
selectedCharactersRange = formattedRange.upperBound..<formattedRange.upperBound
6773
}
6874
}
6975
}
@@ -73,21 +79,25 @@ open class FormattedTextField: UITextField {
7379
guard let text = text else {
7480
return nil
7581
}
76-
var cursorPosition = 0
77-
let unformattedText = self.unformatterText(fromText: text, textMask: textMask, cursorPosition: &cursorPosition)
82+
guard let formatter = textFormatter else {
83+
return text
84+
}
85+
let unformattedText = formatter.unformattedText(from: text)
7886

7987
return unformattedText
8088
}
8189
set(value) {
82-
var cursorPosition = 0
83-
let unformattedText = value ?? ""
84-
let formattedText = self.formattedText(fromText: unformattedText, textMask: textMask, cursorPosition: &cursorPosition)
90+
var formattedText = (value ?? "")
91+
if let formatter = textFormatter {
92+
formattedText = formatter.formattedText(from: formattedText)
93+
}
94+
8595
if formattedText.characters.count > 0 || value != nil {
8696
text = formattedText
8797
} else {
8898
text = nil
8999
}
90-
placeholderLabel.isHidden = (unformattedText.characters.count > 0)
100+
placeholderLabel.isHidden = (formattedText.characters.count > 0)
91101
}
92102
}
93103

@@ -96,7 +106,7 @@ open class FormattedTextField: UITextField {
96106
return super.attributedText
97107
}
98108
set(value) {
99-
assert(false, "masked text field unsupports attributed text")
109+
assertionFailure("masked text field unsupports attributed text")
100110
}
101111
}
102112

@@ -141,13 +151,12 @@ open class FormattedTextField: UITextField {
141151
super.layoutSubviews()
142152

143153
var placeholderFrame = self.placeholderRect(forBounds:bounds)
144-
if let mask = textMask, let firstMaskSymbolRange = mask.range(of: String(maskSymbol)) {
145-
let prefix = mask[mask.startIndex..<firstMaskSymbolRange.lowerBound]
154+
if let text = self.text {
146155
var attributes: [String: Any]? = nil
147156
if let placeholderFont = font {
148157
attributes = [NSFontAttributeName: placeholderFont]
149158
}
150-
let prefixWidth = (prefix as NSString).size(attributes: attributes).width
159+
let prefixWidth = (text as NSString).size(attributes: attributes).width
151160
placeholderFrame.origin.x += prefixWidth
152161
placeholderFrame.size.width -= prefixWidth
153162
}
@@ -156,9 +165,6 @@ open class FormattedTextField: UITextField {
156165

157166
// MARK: - Private
158167

159-
private var maskSymbol: Character {
160-
return type(of: self).maskSymbol
161-
}
162168
private let placeholderLabel: UILabel
163169

164170
private lazy var delegateProxy: TextFieldDelegateProxy = {
@@ -174,137 +180,52 @@ open class FormattedTextField: UITextField {
174180
return false
175181
}
176182
}
183+
let text = self.text ?? ""
184+
let charactersRange = text.range(fromUtf16NsRange: range)!
177185

178-
let formattedText = text ?? ""
179-
var charachtersRange = formattedText.nsrange(fromRange: formattedText.range(fromUtf16NsRange: range)!)
180-
181-
var cursorPosition: Int
182-
if string.characters.count > 0 {
183-
cursorPosition = NSMaxRange(charachtersRange)
186+
let unformattedText: String
187+
var unformattedRange: Range<String.Index>
188+
if let formatter = textFormatter {
189+
(unformattedText, unformattedRange) = formatter.unformattedText(from: text, range: charactersRange)
184190
} else {
185-
charachtersRange = deleteBackwardRange(fromRange: charachtersRange)
186-
cursorPosition = charachtersRange.location
191+
unformattedText = text
192+
unformattedRange = charactersRange
187193
}
188194

189-
var unformattedText = unformatterText(fromText: formattedText, textMask: textMask, cursorPosition: &cursorPosition)
190-
let unformattedRange = self.range(fromFormattedRange: charachtersRange)
195+
let isBackspace = (string.characters.count == 0 && unformattedRange.isEmpty)
196+
if isBackspace && unformattedRange.lowerBound != unformattedText.startIndex {
197+
unformattedRange = unformattedText.index(before: unformattedRange.lowerBound)..<unformattedRange.upperBound
198+
}
191199

192200
if let originDelegate = (delegateProxy.delegate as? FormattedTextFieldDelegate),
193201
originDelegate.responds(to: #selector(FormattedTextFieldDelegate.textField(_:shouldChangeUnformattedText:in:replacementString:))) {
194-
195-
let utf16UnformattedRange = unformattedText.utf16Nsrange(fromRange: unformattedText.range(fromNsRange: unformattedRange)!)
202+
let utf16UnformattedRange = unformattedText.utf16Nsrange(fromRange: unformattedRange)
196203
if !originDelegate.textField!(self, shouldChangeUnformattedText:unformattedText, in:utf16UnformattedRange, replacementString: string) {
197204
return false
198205
}
199206
}
200207

201-
unformattedText.replaceSubrange(unformattedText.range(fromNsRange: unformattedRange)!, with: string)
202-
cursorPosition += string.characters.count
203-
if string.characters.count > 0 {
204-
cursorPosition -= unformattedRange.length
205-
}
208+
let newUnformattedText = unformattedText.replacingCharacters(in: unformattedRange, with: string)
209+
let selectionOffset = unformattedText.distance(from: unformattedText.startIndex, to: unformattedRange.lowerBound)
210+
let cursorPosition = newUnformattedText.index(newUnformattedText.startIndex, offsetBy: selectionOffset + string.characters.count)
206211

207-
let newFormattedText = self.formattedText(fromText: unformattedText, textMask: textMask, cursorPosition: &cursorPosition)
208-
text = newFormattedText
209-
placeholderLabel.isHidden = (unformattedText.characters.count > 0)
212+
let formattedText: String
213+
let formattedRange: Range<String.Index>
214+
if let formatter = textFormatter {
215+
(formattedText, formattedRange) = formatter.formattedText(from: newUnformattedText, range: cursorPosition..<cursorPosition)
216+
} else {
217+
formattedText = newUnformattedText
218+
formattedRange = cursorPosition..<cursorPosition
219+
}
220+
self.text = formattedText
221+
selectedCharactersRange = formattedRange.upperBound..<formattedRange.upperBound
210222

211-
cursorPosition = min(cursorPosition, newFormattedText.characters.count)
212-
let cursorIndex = newFormattedText.index(newFormattedText.startIndex, offsetBy: cursorPosition)
213-
selectedCharactersRange = cursorIndex..<cursorIndex
223+
placeholderLabel.isHidden = (newUnformattedText.characters.count > 0)
214224

215225
sendActions(for: .editingChanged)
216226

217227
return false
218228
}
219-
220-
private func unformatterText(fromText text: String, textMask: String?, cursorPosition: inout Int) -> String {
221-
guard let mask = textMask else {
222-
return text
223-
}
224-
let originCursorPosition = cursorPosition
225-
226-
var unformattedText = String()
227-
for i in 0..<(min(mask.characters.count, text.characters.count)) {
228-
let maskCharacter = mask.characters[mask.index(mask.startIndex, offsetBy: i)]
229-
if maskCharacter == maskSymbol {
230-
let textCharacter = text.characters[text.index(text.startIndex, offsetBy: i)]
231-
unformattedText.append(textCharacter)
232-
} else if i < originCursorPosition {
233-
cursorPosition -= 1
234-
}
235-
}
236-
237-
return unformattedText
238-
}
239-
240-
private func formattedText(fromText text: String, textMask: String?, cursorPosition: inout Int) -> String {
241-
guard let mask = textMask else {
242-
return text
243-
}
244-
let originCursorPosition = cursorPosition
245-
246-
var formattedText = String()
247-
var textIndex = 0
248-
for maskCharacter in mask.characters {
249-
if maskCharacter == maskSymbol {
250-
if textIndex >= text.characters.count {
251-
break
252-
}
253-
let textCharacter = text.characters[text.index(text.startIndex, offsetBy: textIndex)]
254-
formattedText.append(textCharacter)
255-
textIndex += 1
256-
} else {
257-
formattedText.append(maskCharacter)
258-
if textIndex <= originCursorPosition {
259-
cursorPosition += 1
260-
}
261-
}
262-
}
263-
264-
return formattedText
265-
}
266-
267-
private func range(fromFormattedRange range: NSRange) -> NSRange {
268-
guard let mask = textMask else {
269-
return range
270-
}
271-
272-
let maskCharactersRange = mask.range(fromNsRange: range)!
273-
var location = 0
274-
for character in mask[mask.startIndex..<maskCharactersRange.lowerBound].characters {
275-
if character == maskSymbol {
276-
location += 1
277-
}
278-
}
279-
var length = 0
280-
for character in mask[maskCharactersRange].characters {
281-
if character == maskSymbol {
282-
length += 1
283-
}
284-
}
285-
return NSMakeRange(location, length)
286-
}
287-
288-
private func deleteBackwardRange(fromRange range: NSRange) -> NSRange {
289-
guard let mask = self.textMask else {
290-
return range
291-
}
292-
let charactersRange = mask.range(fromNsRange: range)!
293-
if mask[charactersRange].contains(String(maskSymbol)) {
294-
return range
295-
}
296-
297-
let searchRange = mask.startIndex..<charactersRange.lowerBound
298-
299-
let deleteRange: Range<String.Index>
300-
if let removedSymbolRange = mask.range(of: String(maskSymbol), options: .backwards, range: searchRange, locale: nil) {
301-
deleteRange = removedSymbolRange.lowerBound..<charactersRange.upperBound
302-
} else {
303-
deleteRange = charactersRange.upperBound..<charactersRange.upperBound
304-
}
305-
306-
return mask.nsrange(fromRange: deleteRange)
307-
}
308229
}
309230

310231
// MARK: - TextFieldDelegateProxy

0 commit comments

Comments
 (0)