Input-mask-ios: ΠΠ΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Π°Ρ маска ΠΏΡ€ΠΈΠΊΡ€Π΅ΠΏΠ»Π΅Π½Π° послС рСдактирования

Π‘ΠΎΠ·Π΄Π°Π½Π½Ρ‹ΠΉ Π½Π° 25 июл. 2018  Β·  11ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ  Β·  Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: RedMadRobot/input-mask-ios

Π£ мСня установлСны ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ маски:
self.login.affineFormats = [ "{+7} ([000]) [000] [00] [00]", "{8} ([000]) [000]-[00]-[00]" ]

Когда я Π½Π°Π±ΠΈΡ€Π°ΡŽ Π½ΠΎΠΌΠ΅Ρ€ Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½Π° с маской +7, Π° Π·Π°Ρ‚Π΅ΠΌ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΡƒΡŽ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² ΠΏΡ€Π΅Π΄Π΅Π»Π°Ρ… ([000]), маска ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π½Π° 8 вмСсто +7, Π° Π·Π°Ρ‚Π΅ΠΌ Π²Π½ΡƒΡ‚Ρ€ΠΈ помСщаСтся 7 ([000])

Π‘Π°ΠΌΡ‹ΠΉ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π”Π°, исправил, спасибо!

ВсС 11 ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

ΠœΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ, Π΅ΡΡ‚ΡŒ способ Β«Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΒ» маску Π² тСкстовом ΠΏΠΎΠ»Π΅ послС Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΎΠ½Π° Π±Ρ‹Π»Π° ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π° любой ΠΈΠ· масок?

ΠŸΡ€ΠΈΠ²Π΅Ρ‚ @MrJox , спасибо Π·Π° ваш ΠΎΡ‚Ρ‡Π΅Ρ‚.
НС ΠΌΠΎΠ³Π»ΠΈ Π±Ρ‹ Π²Ρ‹ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ Ρ‚ΠΎΡ‡Π½ΠΎΠ΅ состояниС тСкста Π²Π½ΡƒΡ‚Ρ€ΠΈ вашСго тСкстового поля, ΠΊΠΎΠ³Π΄Π° Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚ эта ошибка?

Π― смогу ΠΎΡ‚ΡΠ»Π΅Π΄ΠΈΡ‚ΡŒ Π΅Π³ΠΎ ΠΏΠΎΠ·ΠΆΠ΅ сСгодня, Π° Π·Π°Ρ‚Π΅ΠΌ ΠΏΠΎΠΌΠΎΡ‡ΡŒ Π²Π°ΠΌ с Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ.

НС совсСм ΡƒΠ²Π΅Ρ€Π΅Π½, Ρ‡Ρ‚ΠΎ Π²Ρ‹ ΠΏΠΎΠ΄Ρ€Π°Π·ΡƒΠΌΠ΅Π²Π°Π΅Ρ‚Π΅ ΠΏΠΎΠ΄ состояниями, Π½ΠΎ Π²ΠΎΡ‚ Ρ‡Ρ‚ΠΎ происходит:

Π£ мСня Π΅ΡΡ‚ΡŒ Π½ΠΎΠΌΠ΅Ρ€ Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½Π°, ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½Ρ‹ΠΉ ΠΈΠ· кСша ΠΈ вставлСнный Π² тСкстовоС ΠΏΠΎΠ»Π΅ (это ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ΅ тСкстовоС ΠΏΠΎΠ»Π΅ JVFloat).
НомСр Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½Π° +7 (926) 000-00-00 ΠΈ Ρ‚ΠΎΠ³Π΄Π° я ΡΡ‚Π°Π²Π»ΡŽ ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ послС '2' ΠΈ ΡƒΠ΄Π°Π»ΡΡŽ этот Π½ΠΎΠΌΠ΅Ρ€. Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΌΠΎΠΉ тСкстовый ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚ прСобразуСтся Π² 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
с клавишСй Backspace: 8 (796) 000-00-00

Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅:
Π’Π΅ΠΏΠ΅Ρ€ΡŒ, ΠΏΠΎΡ…ΠΎΠΆΠ΅, это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, ΠΎΠ΄Π½Π°ΠΊΠΎ, ΠΊΠΎΠ³Π΄Π° я ΠΏΡ‹Ρ‚Π°ΡŽΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ строку Π² тСкстовом ΠΏΠΎΠ»Π΅, Ρƒ мСня остаСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ символ Β«+Β», Π° Π·Π°Ρ‚Π΅ΠΌ ΠΌΠΎΠ΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π²Ρ‹Π»Π΅Ρ‚Π°Π΅Ρ‚, ΠΊΠΎΠ³Π΄Π° я ΠΏΡ‹Ρ‚Π°ΡŽΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ Π΅Π³ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ

Π’Π΅ΠΌΠ° 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 содСрТит это ΠΊΠ°ΠΊ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ.

Π‘Ρ‹Π»Π° Π»ΠΈ эта страница ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ?
0 / 5 - 0 Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΠΈ

Π‘ΠΌΠ΅ΠΆΠ½Ρ‹Π΅ вопросы

beltik picture beltik  Β·  6ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ

Robuske picture Robuske  Β·  4ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ

TikhonovAlexander picture TikhonovAlexander  Β·  3ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ

razalur picture razalur  Β·  3ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ

LinusGeffarth picture LinusGeffarth  Β·  4ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ