Input-mask-ios: Topeng yang salah terpasang setelah diedit

Dibuat pada 25 Jul 2018  ·  11Komentar  ·  Sumber: RedMadRobot/input-mask-ios

Saya memiliki set topeng berikut:
self.login.affineFormats = [ "{+7} ([000]) [000] [00] [00]", "{8} ([000]) [000]-[00]-[00]" ]

Ketika saya mengetik nomor telepon dengan topeng +7 dan kemudian mengedit nilainya di dalam ([000]) topeng beralih ke 8 alih-alih +7 dan kemudian 7 dimasukkan ke dalam ([000])

enhancement

Komentar yang paling membantu

Ya sudah diperbaiki, terima kasih!

Semua 11 komentar

Mungkin ada cara untuk "mengunci" topeng di textField setelah diinisialisasi dengan topeng apa pun?

Hai @MrJox , terima kasih atas laporannya.
Bisakah Anda memberikan status teks yang tepat di dalam bidang teks Anda saat kesalahan ini terjadi?

Saya akan dapat melacaknya nanti hari ini dan kemudian membantu Anda dengan solusinya.

Tidak sepenuhnya yakin apa yang Anda maksud dengan negara bagian, tetapi inilah yang terjadi:

Saya memiliki nomor telepon yang dibaca dari cache dan dimasukkan ke dalam textField (Ini adalah textField JVFloat khusus).
Nomor teleponnya adalah +7 (926) 000-00-00 dan kemudian saya mengatur penunjuk setelah '2' dan menghapus nomor ini. Akibatnya, konten textView saya diubah menjadi 8 (796) 000-00-00.

Jadi untuk beberapa alasan itu menghapus + dan memperlakukan 7 bukan sebagai topeng tetapi sebagai bagian sebenarnya dari nomor telepon dan memasukkannya ke dalam tanda kurung dan kemudian menambahkan topeng 8.

Ya, deskripsi Anda berfungsi untuk saya, terima kasih!
Tetap disini.

@MrJox saat saya sedang mengerjakan beberapa peningkatan dan perbaikan yang diperlukan oleh pembaruan perpustakaan terakhir kami, saya telah mengumpulkan solusi cepat untuk masalah Anda.

Secara efektif, ini adalah modifikasi PolyMaskTextFieldDelegate dengan metode penghitungan afinitas khusus. Alih-alih afinitas teks keseluruhan dengan topeng, metode ini membandingkan panjang persimpangan awalan, jumlah karakter.

Kode ini atau versi modifikasinya mungkin menemukan jalannya ke sumber perpustakaan pada akhirnya.

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, jadi sekarang saya melakukan ini:
<strong i="6">@IBOutlet</strong> var loginListener: PrefixAffinityMaskedTextFieldDelegate!

Tapi itu tetap tidak akan berhasil. Apa yang saya perhatikan, jika Anda menghapus digit dalam tanda kurung dengan tombol Del, digit tersebut akan dihapus dan sekarang satu digit lebih sedikit di dalam tanda kurung namun ketika saya menghapus dengan tombol Backspace, itu menempatkan/memindahkan digit dari kiri atau digit dari kanan jadi jumlah digit di dalam kurung tetap tidak berubah.

Apa yang saya maksud:
asli: +7 (916) 000-00-00
dengan kunci del: +7 (96) 000-00-00
dengan tombol spasi mundur: 8 (796) 000-00-00

Koreksi:
Sekarang tampaknya berfungsi, namun ketika saya mencoba menghapus string di textField, saya hanya memiliki karakter '+' yang tersisa dan kemudian aplikasi saya mogok ketika saya mencoba menghapusnya dengan

Utas 1: Kesalahan fatal: tidak dapat bertambah melampaui indeks akhir

@MrJox Saya tidak yakin saya mengerti Anda dengan benar.
Perhatikan, tidak ada kunci Del di iOS, hanya Backspace . Jangan biarkan emulasi keyboard virtual menipu Anda.

Mengenai kesalahan terakhir Anda, Anda dapat mempertimbangkan tambalan berikut untuk kasus tepi:

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]
    }

}

Ya sudah diperbaiki, terima kasih!

@MrJox Saya akan menutup masalah ini dengan pembaruan perpustakaan berikutnya.
Saya sedang mempertimbangkan untuk menambahkan ini ke sumber perpustakaan sebagai strategi alternatif.

@MrJox dioptimalkan & versi yang disederhanakan:

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 berisi ini sebagai fitur.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat