Tengo las siguientes máscaras establecidas:
self.login.affineFormats = [
"{+7} ([000]) [000] [00] [00]",
"{8} ([000]) [000]-[00]-[00]"
]
Cuando escribo un número de teléfono con máscara +7 y luego edito el valor dentro ([000]), la máscara cambia a 8 en lugar de +7 y luego se coloca 7 dentro ([000])
¿Tal vez haya una forma de "bloquear" la máscara en textField una vez que se inicializó con alguna de las máscaras?
Hola @MrJox , gracias por tu informe.
¿Podría proporcionar los estados exactos del texto dentro de su campo de texto cuando ocurre este error?
Podré rastrearlo más tarde hoy y luego ayudarlo con la solución.
No estoy absolutamente seguro de lo que quiere decir con estados, pero esto es lo que sucede:
Tengo un número de teléfono leído del caché e insertado en textField (es un JVFloat textField personalizado).
El número de teléfono es +7 (926) 000-00-00 y luego configuro el puntero después de '2' y elimino este número. Como resultado, mi contenido de vista de texto se convierte en 8 (796) 000-00-00.
Entonces, por alguna razón, elimina + y trata a 7 no como una máscara, sino como una parte real del número de teléfono, lo pone entre paréntesis y luego agrega la máscara 8.
Sí, tu descripción funciona para mí, ¡gracias!
Manténganse al tanto.
@MrJox mientras trabajo en algunas mejoras y correcciones requeridas por nuestra última actualización de la biblioteca, he creado una solución rápida para su problema.
Efectivamente, es una modificación de PolyMaskTextFieldDelegate
con un método de cálculo de afinidad personalizado. En lugar de una afinidad general del texto con la máscara, este método compara la longitud de la intersección de los prefijos, el recuento de caracteres.
Este código o su versión modificada podría llegar a las fuentes de la biblioteca con el tiempo.
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)
}
}
Hm, entonces ahora hago esto:
<strong i="6">@IBOutlet</strong> var loginListener: PrefixAffinityMaskedTextFieldDelegate!
Pero todavía no funcionará. Lo que noté, si elimina dígitos entre paréntesis con la tecla Supr, el dígito se elimina y ahora es un dígito menos entre paréntesis; sin embargo, cuando elimino con el botón Retroceso, coloca/mueve el dígito de la izquierda o el dígito de la derecha, por lo que la cantidad de dígitos dentro de los corchetes permanece sin cambios.
Lo que quiero decir:
originales: +7 (916) 000-00-00
con tecla del: +7 (96) 000-00-00
con tecla de retroceso: 8 (796) 000-00-00
Corrección:
Ahora parece funcionar, sin embargo, cuando intento eliminar la cadena en el campo de texto, solo me queda el carácter '+' y luego mi aplicación falla cuando intento eliminarla con
Subproceso 1: error fatal: no se puede incrementar más allá de endIndex
@MrJox No estoy seguro de haberte entendido correctamente.
Tenga en cuenta que no hay una tecla Del
en iOS, solo Backspace
. No dejes que la emulación de teclado virtual te engañe.
Con respecto a su último error, puede considerar el siguiente parche para el caso de borde:
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]
}
}
Sí, lo solucionó, ¡gracias!
@MrJox Cerraré este problema con la próxima actualización de la biblioteca.
Estoy considerando agregar esto a las fuentes de la biblioteca como una estrategia alternativa.
@MrJox versión optimizada y simplificada:
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
contiene esto como una característica.
Comentario más útil
Sí, lo solucionó, ¡gracias!