Input-mask-ios: تم إرفاق قناع خاطئ بعد التحرير

تم إنشاؤها على ٢٥ يوليو ٢٠١٨  ·  11تعليقات  ·  مصدر: RedMadRobot/input-mask-ios

لدي مجموعة الأقنعة التالية:
self.login.affineFormats = [ "{+7} ([000]) [000] [00] [00]", "{8} ([000]) [000]-[00]-[00]" ]

عندما أكتب رقم هاتف مع قناع +7 ثم قم بتحرير القيمة داخل ([000]) تبديل القناع إلى 8 بدلاً من +7 ثم يتم وضع 7 بالداخل ([000])

enhancement

التعليق الأكثر فائدة

نعم لقد أصلحته ، شكرا!

ال 11 كومينتر

ربما توجد طريقة "لقفل" القناع في حقل النص بمجرد تهيئته بأي من الأقنعة؟

مرحبًا MrJox ، شكرًا لتقريرك.
هل يمكنك تقديم الحالات الدقيقة للنص داخل حقل النص عند حدوث هذا الخطأ؟

سأكون قادرًا على تتبعها لاحقًا اليوم ثم أساعدك في الحل.

لست متأكدًا تمامًا مما تقصده بكلمات ولكن إليك ما يحدث:

لدي رقم هاتف تمت قراءته من ذاكرة التخزين المؤقت وتم إدراجه في حقل النص (إنه حقل نصي JVFloat مخصص).
رقم الهاتف هو +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 لست متأكدًا من أنني فهمتك بشكل صحيح.
لاحظ أنه لا يوجد مفتاح Del على iOS ، فقط 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 يحتوي على هذا كميزة.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات