J'ai le jeu de masques suivant :
self.login.affineFormats = [
"{+7} ([000]) [000] [00] [00]",
"{8} ([000]) [000]-[00]-[00]"
]
Lorsque je tape un numéro de téléphone avec un masque +7, puis que je modifie la valeur dans ([000]), le masque passe à 8 au lieu de +7, puis 7 est mis à l'intérieur ([000])
Peut-être existe-t-il un moyen de "verrouiller" le masque dans textField une fois qu'il a été initialisé avec l'un des masques?
Bonjour @MrJox , merci pour votre rapport.
Pourriez-vous s'il vous plaît fournir les états exacts du texte à l'intérieur de votre champ de texte lorsque cette erreur se produit ?
Je pourrai le tracer plus tard dans la journée et vous aider à trouver la solution.
Je ne sais pas exactement ce que vous entendez par états, mais voici ce qui se passe :
J'ai un numéro de téléphone lu à partir du cache et inséré dans textField (c'est un textField JVFloat personnalisé).
Le numéro de téléphone est le +7 (926) 000-00-00, puis je place le pointeur après '2' et supprime ce numéro. En conséquence, mon contenu textView est converti en 8 (796) 000-00-00.
Donc, pour une raison quelconque, il supprime + et traite 7 non pas comme un masque mais comme une partie réelle du numéro de téléphone et le met entre parenthèses, puis ajoute le masque 8.
Oui, votre description fonctionne pour moi, merci!
Restez à l'écoute.
@MrJox pendant que je travaille sur certaines améliorations et correctifs requis par notre dernière mise à jour de la bibliothèque, j'ai mis en place une solution rapide à votre problème.
En fait, il s'agit d'une modification PolyMaskTextFieldDelegate
avec une méthode de calcul d'affinité personnalisée. Au lieu d'une affinité globale du texte avec le masque, cette méthode compare la longueur de l'intersection des préfixes, le nombre de caractères.
Ce code ou sa version modifiée pourrait éventuellement trouver son chemin vers les sources de la bibliothèque.
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, alors maintenant je fais ceci:
<strong i="6">@IBOutlet</strong> var loginListener: PrefixAffinityMaskedTextFieldDelegate!
Mais ça ne marchera toujours pas. Ce que j'ai remarqué, si vous supprimez des chiffres entre parenthèses avec la touche Suppr, le chiffre est supprimé et maintenant c'est un chiffre de moins entre parenthèses, mais lorsque je supprime avec le bouton Retour arrière, il place/déplace soit le chiffre de gauche, soit le chiffre de droite, donc le nombre de chiffres entre parenthèses reste inchangé.
Ce que je veux dire:
d'origine : +7 (916) 000-00-00
avec touche sup: +7 (96) 000-00-00
avec touche retour arrière : 8 (796) 000-00-00
Correction:
Maintenant, cela semble fonctionner, mais lorsque j'essaie de supprimer la chaîne dans le champ de texte, il ne me reste plus que le caractère '+', puis mon application se bloque lorsque j'essaie de la supprimer avec
Thread 1 : Erreur fatale : impossible d'incrémenter au-delà de endIndex
@MrJox Je ne suis pas sûr de vous avoir bien compris.
Attention, il n'y a pas de clé Del
sur iOS, seulement Backspace
. Ne laissez pas l'émulation de clavier virtuel vous tromper.
Concernant votre dernière erreur, vous pouvez envisager le correctif suivant pour le cas limite :
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]
}
}
Oui c'est corrigé, merci !
@MrJox Je fermerai ce problème avec la prochaine mise à jour de la bibliothèque.
J'envisage d'ajouter ceci aux sources de la bibliothèque comme stratégie alternative.
@MrJox version optimisée et simplifiée :
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
contient ceci en tant que fonctionnalité.
Commentaire le plus utile
Oui c'est corrigé, merci !