๋ค์ ๋ง์คํฌ๊ฐ ์ค์ ๋์ด ์์ต๋๋ค.
self.login.affineFormats = [
"{+7} ([000]) [000] [00] [00]",
"{8} ([000]) [000]-[00]-[00]"
]
+7 ๋ง์คํฌ๊ฐ ์๋ ์ ํ๋ฒํธ๋ฅผ ์ ๋ ฅํ ๋ค์ ([000]) ๋ด์ ๊ฐ์ ํธ์งํ๋ฉด ๋ง์คํฌ๊ฐ +7 ๋์ 8๋ก ์ ํ๋๊ณ ๊ทธ ๋ค์ 7์ด ([000]) ์์ ๋ค์ด๊ฐ๋๋ค
๋ง์คํฌ๋ก ์ด๊ธฐํ๋๋ฉด textField์์ ๋ง์คํฌ๋ฅผ "์ ๊ทธ๋" ๋ฐฉ๋ฒ์ด ์์ต๋๊น?
์๋
ํ์ธ์ @MrJox , ๋ณด๊ณ ํด ์ฃผ์
์ ๊ฐ์ฌํฉ๋๋ค.
์ด ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ ํ
์คํธ ํ๋์ ์๋ ํ
์คํธ์ ์ ํํ ์ํ๋ฅผ ์๋ ค์ฃผ์๊ฒ ์ต๋๊น?
์ค๋ ๋์ค์ ์ถ์ ํ ๋ค์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋์๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ํ๊ฐ ๋ฌด์์ ์๋ฏธํ๋์ง ์ ๋์ ์ผ๋ก ํ์ ํ ์ ์์ง๋ง ๋ค์๊ณผ ๊ฐ์ ์ผ์ด ๋ฐ์ํฉ๋๋ค.
์บ์์์ ์ ํ๋ฒํธ๋ฅผ ์ฝ๊ณ textField์ ์ฝ์
ํ์ต๋๋ค(์ฌ์ฉ์ ์ง์ JVFloat textField์
๋๋ค).
์ ํ๋ฒํธ๋ +7 (926) 000-00-00 ์ธ๋ฐ '2' ๋ค์ ํฌ์ธํฐ๋ฅผ ๋๊ณ ์ด ๋ฒํธ๋ฅผ ์ง์๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ๋ด textView ์ฝํ
์ธ ๋ 8 (796) 000-00-00์ผ๋ก ๋ณํ๋ฉ๋๋ค.
๊ทธ๋์ ์ด๋ค ์ด์ ๋ก +๋ฅผ ์ญ์ ํ๊ณ 7์ ๋ง์คํฌ๊ฐ ์๋ ์ ํ ๋ฒํธ์ ์ค์ ๋ถ๋ถ์ผ๋ก ์ทจ๊ธํ๊ณ ๋๊ดํธ์ ๋ฃ์ ๋ค์ 8 ๋ง์คํฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์, ๊ทํ์ ์ค๋ช
์ ์ ์๊ฒ ํจ๊ณผ์ ์
๋๋ค. ๊ฐ์ฌํฉ๋๋ค!
๊ณ์ ์ง์ผ๋ด ์ฃผ์ธ์.
@MrJox ๋ง์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ๋ฐ์ดํธ์ ํ์ํ ๋ช ๊ฐ์ง ๊ฐ์ ์ฌํญ๊ณผ ์์ ์ฌํญ์ ์์ ํ๋ ๋์ ๋ฌธ์ ์ ๋ํ ๋น ๋ฅธ ์๋ฃจ์ ์ ๋ง๋ จํ์ต๋๋ค.
์ฌ์ค์ ์ฌ์ฉ์ ์ง์ ์ ํธ๋ ๊ณ์ฐ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ PolyMaskTextFieldDelegate
์์ ์
๋๋ค. ๋ง์คํฌ์์ ์ ์ฒด ํ
์คํธ ์ ํธ๋ ๋์ ์ด ๋ฉ์๋๋ ์ ๋์ฌ ๊ต์ฐจ ๊ธธ์ด, ๋ฌธ์ ์๋ฅผ ๋น๊ตํฉ๋๋ค.
์ด ์ฝ๋ ๋๋ ์์ ๋ ๋ฒ์ ์ ๊ฒฐ๊ตญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ค์ ๋๋ฌํ ์ ์์ต๋๋ค.
import Foundation
import UIKit
import InputMask
<strong i="10">@IBDesignable</strong>
class PrefixAffinityMaskedTextFieldDelegate: MaskedTextFieldDelegate {
public var affineFormats: [String]
public init(primaryFormat: String, affineFormats: [String]) {
self.affineFormats = affineFormats
super.init(format: primaryFormat)
}
public override init(format: String) {
self.affineFormats = []
super.init(format: format)
}
override open func put(text: String, into field: UITextField) {
let mask: Mask = pickMask(
forText: text,
caretPosition: text.endIndex,
autocomplete: autocomplete
)
let result: Mask.Result = mask.apply(
toText: CaretString(
string: text,
caretPosition: text.endIndex
),
autocomplete: autocomplete
)
field.text = result.formattedText.string
field.caretPosition = result.formattedText.string.distance(
from: result.formattedText.string.startIndex,
to: result.formattedText.caretPosition
)
listener?.textField?(field, didFillMandatoryCharacters: result.complete, didExtractValue: result.extractedValue)
}
override open func deleteText(inRange range: NSRange, inTextInput field: UITextInput) -> Mask.Result {
let text: String = replaceCharacters(
inText: field.allText,
range: range,
withCharacters: ""
)
let mask: Mask = pickMask(
forText: text,
caretPosition: text.index(text.startIndex, offsetBy: range.location),
autocomplete: false
)
let result: Mask.Result = mask.apply(
toText: CaretString(
string: text,
caretPosition: text.index(text.startIndex, offsetBy: range.location)
),
autocomplete: false
)
field.allText = result.formattedText.string
field.caretPosition = range.location
return result
}
override open func modifyText(
inRange range: NSRange,
inTextInput field: UITextInput,
withText text: String
) -> Mask.Result {
let updatedText: String = replaceCharacters(
inText: field.allText,
range: range,
withCharacters: text
)
let mask: Mask = pickMask(
forText: updatedText,
caretPosition: updatedText.index(updatedText.startIndex, offsetBy: field.caretPosition + text.count),
autocomplete: autocomplete
)
let result: Mask.Result = mask.apply(
toText: CaretString(
string: updatedText,
caretPosition: updatedText.index(updatedText.startIndex, offsetBy: field.caretPosition + text.count)
),
autocomplete: autocomplete
)
field.allText = result.formattedText.string
field.caretPosition = result.formattedText.string.distance(
from: result.formattedText.string.startIndex,
to: result.formattedText.caretPosition
)
return result
}
func pickMask(forText text: String, caretPosition: String.Index, autocomplete: Bool) -> Mask {
let primaryAffinity: Int = calculateAffinity(
ofMask: mask,
forText: text,
caretPosition: caretPosition,
autocomplete: autocomplete
)
var masks: [(Mask, Int)] = affineFormats.map { (affineFormat: String) -> (Mask, Int) in
let mask: Mask = try! Mask.getOrCreate(withFormat: affineFormat, customNotations: customNotations)
let affinity: Int = calculateAffinity(
ofMask: mask,
forText: text,
caretPosition: caretPosition,
autocomplete: autocomplete
)
return (mask, affinity)
}
masks.sort { (left: (Mask, Int), right: (Mask, Int)) -> Bool in
return left.1 > right.1
}
var insertIndex: Int = -1
for (index, maskAffinity) in masks.enumerated() {
if primaryAffinity >= maskAffinity.1 {
insertIndex = index
break
}
}
if (insertIndex >= 0) {
masks.insert((mask, primaryAffinity), at: insertIndex)
} else {
masks.append((mask, primaryAffinity))
}
return masks.first!.0
}
func calculateAffinity(
ofMask mask: Mask,
forText text: String,
caretPosition: String.Index,
autocomplete: Bool
) -> Int {
return mask.apply(
toText: CaretString(
string: text,
caretPosition: caretPosition
),
autocomplete: autocomplete
).formattedText.string.prefixIntersection(with: text).count
}
func replaceCharacters(inText text: String, range: NSRange, withCharacters newText: String) -> String {
if 0 < range.length {
let result = NSMutableString(string: text)
result.replaceCharacters(in: range, with: newText)
return result as String
} else {
let result = NSMutableString(string: text)
result.insert(newText, at: range.location)
return result as String
}
}
}
extension String {
func prefixIntersection(with string: String) -> Substring {
let lhsStartIndex = startIndex
var lhsEndIndex = startIndex
let rhsStartIndex = string.startIndex
var rhsEndIndex = string.startIndex
while (self[lhsStartIndex...lhsEndIndex] == string[rhsStartIndex...rhsEndIndex]) {
lhsEndIndex = lhsEndIndex != endIndex ? index(after: lhsEndIndex) : endIndex
rhsEndIndex = rhsEndIndex != string.endIndex ? string.index(after: rhsEndIndex) : endIndex
if (lhsEndIndex == endIndex || rhsEndIndex == string.endIndex) {
return self[lhsStartIndex..<lhsEndIndex]
}
}
return self[lhsStartIndex..<lhsEndIndex]
}
}
extension UITextInput {
var allText: String {
get {
guard let all: UITextRange = allTextRange
else { return "" }
return self.text(in: all) ?? ""
}
set(newText) {
guard let all: UITextRange = allTextRange
else { return }
self.replace(all, withText: newText)
}
}
var caretPosition: Int {
get {
if let responder = self as? UIResponder {
// Workaround for non-optional `beginningOfDocument`, which could actually be nil if field doesn't have focus
guard responder.isFirstResponder
else { return allText.count }
}
if let range: UITextRange = selectedTextRange {
let selectedTextLocation: UITextPosition = range.start
return offset(from: beginningOfDocument, to: selectedTextLocation)
} else {
return 0
}
}
set(newPosition) {
if let responder = self as? UIResponder {
// Workaround for non-optional `beginningOfDocument`, which could actually be nil if field doesn't have focus
guard responder.isFirstResponder
else { return }
}
if newPosition > allText.count {
return
}
let from: UITextPosition = position(from: beginningOfDocument, offset: newPosition)!
let to: UITextPosition = position(from: from, offset: 0)!
selectedTextRange = textRange(from: from, to: to)
}
}
var allTextRange: UITextRange? {
return self.textRange(from: self.beginningOfDocument, to: self.endOfDocument)
}
}
ํ , ์ด์ ๋๋ ์ด๊ฒ์ํฉ๋๋ค :
<strong i="6">@IBOutlet</strong> var loginListener: PrefixAffinityMaskedTextFieldDelegate!
ํ์ง๋ง ์ฌ์ ํ ์๋ํ์ง ์์ต๋๋ค. ๋ด๊ฐ ์์ ์ฐจ๋ฆฐ ๊ฒ์ Del ํค๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ดํธ ์์ ์ซ์๋ฅผ ์ญ์ ํ๋ฉด ์ซ์๊ฐ ์ญ์ ๋๊ณ ์ด์ ๋๊ดํธ ์์ ์ซ์๊ฐ ํ๋ ์ ์ต๋๋ค. ๊ทธ๋ฌ๋ Backspace ๋ฒํผ์ผ๋ก ์ญ์ ํ ๋ ์ผ์ชฝ์์ ์ซ์ ๋๋ ์ค๋ฅธ์ชฝ์์ ์ซ์๋ฅผ ๋ฐฐ์น/์ด๋ํฉ๋๋ค. ๋๊ดํธ ์์ ์๋ฆฟ์๋ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค.
๋ด ๋ง์:
์๋ณธ: +7 (916) 000-00-00
del ํค ์ฌ์ฉ: +7 (96) 000-00-00
๋ฐฑ์คํ์ด์ค ํค ์ฌ์ฉ: 8 (796) 000-00-00
๋ณด์ :
์ด์ ์๋ํ๋ ๊ฒ ๊ฐ์ง๋ง textField์์ ๋ฌธ์์ด์ ์ญ์ ํ๋ ค๊ณ ํ ๋ '+' ๋ฌธ์๋ง ๋จ์ ์๊ณ ๋ค์์ผ๋ก ์ญ์ ํ๋ ค๊ณ ํ ๋ ์ฑ์ด ์ถฉ๋ํฉ๋๋ค.
์ค๋ ๋ 1: ์น๋ช ์ ์ธ ์ค๋ฅ: endIndex ์ด์์ผ๋ก ์ฆ๊ฐํ ์ ์์ต๋๋ค.
@MrJox ๋ด๊ฐ ๋น์ ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ดํดํ๋์ง ํ์ ํ ์ ์์ต๋๋ค.
iOS์๋ Del
ํค๊ฐ ์๊ณ $ Backspace
์์ต๋๋ค. ๊ฐ์ ํค๋ณด๋ ์๋ฎฌ๋ ์ด์
์ ์์ง ๋ง์ญ์์ค.
๋ง์ง๋ง ์ค๋ฅ์ ๊ด๋ จํ์ฌ ๊ฒฝ์ฐ์ ๋ฐ๋ผ ๋ค์ ํจ์น๋ฅผ ๊ณ ๋ คํ ์ ์์ต๋๋ค.
extension String {
func prefixIntersection(with string: String) -> Substring {
guard !self.isEmpty && !string.isEmpty
else { return "" }
let lhsStartIndex = startIndex
var lhsEndIndex = startIndex
let rhsStartIndex = string.startIndex
var rhsEndIndex = string.startIndex
while (self[lhsStartIndex...lhsEndIndex] == string[rhsStartIndex...rhsEndIndex]) {
lhsEndIndex = lhsEndIndex != endIndex ? index(after: lhsEndIndex) : endIndex
rhsEndIndex = rhsEndIndex != string.endIndex ? string.index(after: rhsEndIndex) : endIndex
if (lhsEndIndex == endIndex || rhsEndIndex == string.endIndex) {
return self[lhsStartIndex..<lhsEndIndex]
}
}
return self[lhsStartIndex..<lhsEndIndex]
}
}
๋ค ํด๊ฒฐํ์ต๋๋ค ๊ฐ์ฌํฉ๋๋ค!
@MrJox ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์
๋ฐ์ดํธ๋ก ์ด ๋ฌธ์ ๋ฅผ ์ข
๋ฃํ๊ฒ ์ต๋๋ค.
๋์ ์ ๋ต์ผ๋ก ์ด๊ฒ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ค์ ์ถ๊ฐํ๋ ๊ฒ์ ๊ณ ๋ คํ๊ณ ์์ต๋๋ค.
@MrJox ์ต์ ํ ๋ฐ ๋จ์ํ ๋ฒ์ :
extension String {
func prefixIntersection(with string: String) -> Substring {
var lhsIndex = startIndex
var rhsIndex = string.startIndex
while lhsIndex != endIndex && rhsIndex != string.endIndex {
if self[...lhsIndex] == string[...rhsIndex] {
lhsIndex = index(after: lhsIndex)
rhsIndex = string.index(after: rhsIndex)
} else {
return self[..<lhsIndex]
}
}
return self[..<lhsIndex]
}
}
4.0.0
์๋ ์ด๊ฒ์ ๊ธฐ๋ฅ์ผ๋ก ํฌํจํฉ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๋ค ํด๊ฒฐํ์ต๋๋ค ๊ฐ์ฌํฉ๋๋ค!