IOS-Feldvalidierung - schnell und einfach

BildDie Validierung von Eingabefeldern ist möglicherweise die häufigste Aufgabe in mobilen Anwendungen. Jede Anwendung, die die Form der Autorisierung und Registrierung hat, verfügt auch über eine Reihe von Eingabewerkzeugen des Benutzers, die die erwartete sadistisch-raffinierte Aufregung der Tester hervorrufen. Eine fortgeschrittene, technisch kompetente Entwicklergemeinschaft hat gelernt, wie man effektiv mit ihnen umgeht, indem reguläre Ausdrücke in den Quellcode eingebettet werden.

Wenn Sie sich auf die Registrierung und Autorisierung beschränken müssen, müssen Sie in der Regel keine besonderen Anstrengungen unternehmen, um Ihr Ziel zu erreichen. Der Code wird fast unverändert von einem Projekt in ein anderes übertragen. Und vor allem, ohne die Arbeitszeit für die weitere Pflege dieses Codes zu erhöhen. Aber die Welt wäre nicht so einfach, wenn es nicht die kreativen Impulse von UI / UX-Designern gäbe, die entgegen der etablierten Tradition, Logik und dem gesunden Menschenverstand dazu neigen, neue Wege der Interaktion mit dem Endbenutzer zu erfinden und auf das Formular mehrere ihrer Meinung nach notwendige Kontrollen und Zugänglichkeit zu setzen Dies hängt von den kartesischen Bedingungen für die Überprüfung der Gültigkeit einer großen Anzahl von Eingabefeldern und anderen Steuerelementen ab. Leider kann diese Situation kaum als selten bezeichnet werden.

Die Irritation des Entwicklers wächst proportional dazu, wie oft er gegen DRY-Prinzipien verstoßen muss: Einerseits lässt der professionelle Stolz keine Kompromisse zu, und andererseits ist das Kopieren und Einfügen von Code der effektivste Weg, um einen langen Testzyklus zu vermeiden - das Debuggen. Eintönig kopierter Code ist viel einfacher zu pflegen als ein ideologisch verifiziertes einzigartiges "Fahrrad". Die Suche nach einer Alternative verschwendet nicht nur die Kreativität des Entwicklers, sondern fügt dem Projekt auch Abhängigkeiten hinzu.

Gleichzeitig bietet das iOS SDK einige äußerst unterschätzte Funktionen, die sich leicht auf viele Aufgaben skalieren lassen, die nicht nur mit der Validierung zusammenhängen. Die deklarative Programmierung vereinfacht das Leben des Entwicklers erheblich. Natürlich kennt der Autor das unerbittliche Lager der LiebendenFreunde der Verwendung von "unverdünntem" Code, aber seit der Autor des Artikels begann, professionelle Schnittstellen durch die Entwicklung einer grafischen Benutzeroberfläche für MS DOS zu entwickeln, besteht kein großer Wunsch, Zeit damit zu verschwenden, eine weitere perfekte Klasse zu erstellen, und wenn Sie sie mit gleichem Wert verwenden können Maus - Die Maus wird bevorzugt. Dementsprechend wird beschrieben, wie die Codemenge minimiert werden kann, um den Entwicklungsprozess zu beschleunigen und zu vereinfachen.

Haftungsausschluss:
, . . , — .

Eine typische Mindestaufgabe lautet wie folgt:

Es gibt Anmelde- und Passworteingabefelder sowie eine Autorisierungsschaltfläche. Es ist erforderlich, dass die Berechtigungsschaltfläche den Status (isEnable) ändert, je nachdem, was in den Eingabefeldern enthalten ist.

Eine etwas fortgeschrittenere Version dieser Aufgabe sieht folgendermaßen aus:

Wir haben die Eingabefelder für E-Mail, Passwort und Telefonnummer sowie zwei Schaltflächen - Registrierung und Anforderung eines SMS-Codes für das eingegebene Telefon. Die Registrierungsschaltfläche sollte nur verfügbar sein, wenn in jedes der Felder die richtigen Daten eingegeben wurden. Die Schaltfläche zur Codeanforderung befindet sich, wenn das Feld für die Telefonnummer gültig ist.

Typische Lösung- Erstellen von voneinander abhängigen Flags durch Kombinieren der Anweisungen "if" und "switch" in einem View Controller. Die Schwierigkeit steigt mit der Anzahl der am Prozess beteiligten Kontrollen. Eine viel fortgeschrittenere Lösung wäre die Erstellung einer Zustandsmaschine. Die Lösung ist ausgezeichnet - aber zeitaufwändig, zusätzlich mit einer hohen Eintrittsschwelle - und dies ist keineswegs das, was ich dem faulen (AKA "true") Entwickler implementieren möchte.

Lema über einen faulen Entwickler
, , . (« » ), , .

Die allgemeine Idee des Vorschlags lautet wie folgt:

Es gibt eine Klasse für das Eingabefeld und eine Klasse für den Validierungsmanager. Das Eingabefeld wird erwartungsgemäß von UITextField geerbt. Validierungsmanager - von UIControl geerbt, enthält eine Sammlung validierter Elemente (es ist überhaupt nicht erforderlich, Nachkommen von UITextField zu sein) und implementiert das "Beobachter" -Muster. Darüber hinaus fungiert er als Manager für andere Steuerelemente, deren Zugriffsstatus geändert werden muss, wenn sich der Status validierter Elemente ändert. Mit anderen Worten, wenn das Feld eine ungültige E-Mail enthält, sollte die Registrierungsschaltfläche nicht verfügbar sein.



Die Eingabefelder sind selbstvalidierend - sie können die Frage beantworten, ob die darin enthaltenen Daten gültig sind, und ihren Status ändern. Einige Entwickler bevorzugen die Verwendung einer separaten Validator-Klasse (und ihrer Nachkommen), um Eingabefelder zu validieren. Diese Vorgehensweise macht es jedoch unmöglich, die deklarative und skalierbare Portabilität des Codes vollständig zu nutzen. Für uns ist es wichtig, dass das Eingabefeld oder ein anderes validiertes Steuerelement eine einfache Schnittstelle des Formulars unterstützt:

@objc protocol IValidatable : class {
    varisValid: Bool { get }
}

Um Ihrem Projekt eine Validierung hinzuzufügen, müssen Sie 4 Dateien aus dem Beispiel im GitHub-Repository hinzufügen .

  • Die DSTextField- Klasse implementiert ein Eingabefeld mit einer Angabe des Validierungsprozesses.
  • Die DSValidationManager- Klasse ist ein Beobachter, der die von uns benötigten Funktionen bereitstellt.
  • Erweiterung StringOptional + Utils - enthält nur eine Methode, die mithilfe eines regulären Ausdrucks überprüft, ob der Text der aktuellen Zeile gültig ist.
  • UIView + Layer ist nur eine bequeme Möglichkeit, einem Nachkommen von UIView einen Rahmen mit einer bestimmten Breite hinzuzufügen.

Wenn Sie die Validierung für jedes andere Steuerelement auf die gleiche Weise implementieren möchten, benötigen Sie höchstwahrscheinlich nur den DSValidationManager. Der Rest wird nur zu Ihrer Bequemlichkeit verwendet.

Der Validierungsprozess wird im Video (gif) gezeigt.


Wie Sie sehen können, befindet sich das Feld vor dem Start der Eingabe im Standardzustand, und die Validierung erfolgt bei der Dateneingabe. Wenn Sie das Feld verlassen, bevor die Validierung abgeschlossen ist, wird auf dem Feld eine Beschriftung angezeigt, die über den Fehler informiert. Die Registrierungsschaltfläche ist erst verfügbar, wenn beide Felder gültig sind.

Eine ähnliche Situation entwickelt sich auf dem zweiten Bildschirm (der erst nach Aktivierung der Registrierung verfügbar wird). Auf dem zweiten Bildschirm erfolgt die Überprüfung von Feldern und Schaltflächen jedoch unabhängig voneinander. Die Registrierungsschaltfläche wird nur verfügbar, wenn alle Felder fehlgeschlagen sind. Gleichzeitig ist die Schaltfläche zum Senden von SMS bereits verfügbar, wenn das Feld mit der Telefonnummer gültig ist.

Und jetzt der Trick:Das gesamte Projekt enthält nur eine Klasse für den Ansichtscontroller, dieselbe, die Xcode beim Erstellen des Projekts aus der Vorlage erstellt hat. Es ist völlig leer. Aber auch es wird nicht benutzt. Dies ist mit Hilfe des Dispatchers nicht schwer zu überprüfen.



Ein aufmerksamer Leser bemerkte, dass sich rechts neben dem Antwortenden ein Objekt aus der Standard-Xcode-Komponentenpalette befindet. Dafür wird der Class-Parameter von der DSValidationManager- Klasse überschrieben . Der gleiche Trick wurde für Eingabefelder ausgeführt, mit dem einzigen Unterschied, dass dort die DSTextField- Klasse verwendet wird .

Jetzt läuft unsere gesamte Programmierung auf die folgenden einfachen Schritte hinaus:

  1. Verknüpfen Sie die verifiedControls-Auflistung aus dem ValidationManager mit Eingabefeldern.


  2. Verknüpfen Sie die ManagedControls-Auflistung von ValidationManager mit der Schaltfläche, die Sie verwalten möchten.


  3. Ordnen Sie dem ValidationManager Eingabefelder in die entgegengesetzte Richtung zu und machen Sie ihn zu einem Delegaten.

  4. Legen Sie im Eingabefeld einen regulären Ausdruck für die Validierung und eine Fehlermeldung sowie andere benutzerdefinierte und Standardeigenschaften über den Standard-Xcode-Manager fest. Grundsätzlich kann alles außer dem regulären Ausdruck "wie es ist" belassen werden. Sie müssen lediglich die Tastatur verwenden und dann die Formel aus Tyrnet kopieren und einfügen. Wenn die Fehlermeldung fehlt, wird sie einfach nicht auf dem Bildschirm angezeigt.


Der DSValidationManager- Code ist hässlich primitiv.

import UIKit

@objc protocol IValidationManager : class {
    func verificated()
}

@objc protocol IValidatable : class {
    var isValid: Bool { get }
}

class DSValidationManager : UIControl, IValidationManager
{
    @IBOutlet var verifiedControls: [UIControl]?
    @IBOutlet var managedControls: [UIControl]?

    private (set) var valid : Bool = false {
        didSet {
            self.sendActions(for: .valueChanged)
        }
    }
    
    overrideinit(frame: CGRect) {
        super.init(frame: frame)
        self.verificated()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.verificated()
    }

    func verificated() {
        self.checkVerifiedControls()
        self.controlsAction()
    }

    private func checkVerifiedControls() {
        guard let list:[IValidatable] = self.verifiedControls?.filter({$0 isIValidatable}) as? [IValidatable]
            else { return }
        self.valid = list.filter({!$0.isValid}).count==0
    }
    
    private func controls Action() {
        guard let list = self.managedControls else { return }
        for item in list {
            item.isEnabled = self.valid
        }
    }
}

Wie Sie sehen können, erhält er eine Benachrichtigung von einer Sammlung und handelt auf eine andere.

Ein Beobachter macht es zu einer Eigenschaft, die wir in diesem Beispiel nicht einmal berücksichtigt haben. Sie können die Verbindung vom ValidationManager-Objekt direkt auf den Controller ziehen, dort ein Ereignis hinzufügen, das dem Klicken auf eine Schaltfläche ähnelt, und den Status des Managers direkt im Controller verarbeiten. Wenn du es wirklich willst. Dies kann nützlich sein, wenn Sie nicht nur das Steuerelement sperren, sondern auch nützliche Arbeit leisten sollten (z. B. einen Marsch spielen oder AlertView anzeigen).

Von allen TextEdit-Codes sollte nur beachtet werden, dass das Feld darauf achten muss, die Eigenschaft " isValid " korrekt festzulegen und die Methode " verified () " abzurufen"Von seinem Delegierten. Beachten Sie jedoch, dass das Eingabefeld keinen Verweis auf das Managerobjekt verwendet, sondern eine Sammlung. Auf diese Weise können Sie ein Eingabefeld an mehrere Manager binden.

Dementsprechend muss die Methode von jedem der Delegierten gezogen werden.

    var isValid: Bool {
        return self.text.verification(self.expression, required:self.valueRequired)
    }

    @IBOutlet var validateDelegates: [IValidationManager]?

    private func handleDelegateAction(_ sender: UITextField) {
        guard let list = self.validateDelegates else { return }

        for validateDelegate in list {
            validateDelegate.verificated()
        }
    }

Wenn Sie jetzt ein neues Formular mit Eingabefeldern erstellen müssen, müssen Sie nicht einmal benutzerdefinierte Eigenschaftswerte festlegen. Es reicht aus, die erforderlichen Runtime-Attribute in großen Mengen zu kopieren und auf ein neues Feld anzuwenden (vergessen Sie nicht, nur den Klassennamen zu ersetzen).


Im Allgemeinen kann der Trick mit dem „Objekt“ im Formularheader viel häufiger verwendet werden als nur zur Feldvalidierung - er reagiert mit MVP auf MVVM ohne Rx. Die häufigste Verwendung ist die Netzwerkfreigabe und die Benachrichtigung über Änderungen im CoreData-Modell.

All Articles