Validação de campo IOS - rápida e fácil

imagemA validação dos campos de entrada é talvez a tarefa mais comum em aplicativos móveis. Cada aplicativo que assume a forma de autorização e registro também possui várias ferramentas de entrada do usuário, que dão origem à expectativa sádica - excitação sofisticada esperada dos testadores. Uma comunidade avançada de desenvolvedores tecnicamente competentes aprendeu a lidar efetivamente com eles incorporando expressões regulares no código-fonte.

Como regra, quando você precisa se limitar ao registro e autorização, não precisa fazer esforços especiais para atingir sua meta - o código é transferido de um projeto para outro, quase sem alterações. E, o mais importante, sem aumentar as horas de trabalho para manutenção adicional deste código. Mas o mundo não seria tão simples se não fosse pelos impulsos criativos dos designers de UI / UX que, ao contrário da tradição, lógica e senso comum estabelecidos, inventem novas maneiras de interagir com o usuário final, colocando no formulário vários, na opinião deles, controles necessários, acessibilidade que depende do conjunto de condições cartesianas para verificar a validade de um grande número de campos de entrada e outros controles de controle. Infelizmente, essa situação dificilmente pode ser chamada de rara.

A irritação do desenvolvedor aumenta proporcionalmente à frequência com que ele tem de violar os princípios DRY: por um lado, o orgulho profissional não permite compromissos e, por outro lado, o código copiar e colar é a maneira mais eficaz de evitar um longo ciclo de testes - a depuração. O código copiado e monótono é muito mais fácil de manter do que a "bicicleta" única ideologicamente verificada. A busca por uma alternativa não apenas desperdiça a criatividade do desenvolvedor, mas também adiciona dependências ao projeto.

Ao mesmo tempo, o iOS SDK apresenta alguns recursos extremamente subestimados que podem ser facilmente escalonados para muitas tarefas relacionadas não apenas à validação - a programação declarativa simplifica bastante a vida do desenvolvedor. Claro, o autor conhece o implacável campo de amantesamigos de usar código "não diluído", mas desde que o autor do artigo começou a desenvolver interfaces profissionais desenvolvendo uma interface gráfica para o MS DOS, não há grande desejo de perder tempo criando outra classe perfeita, e se você pode usá-la com o mesmo valor mouse - a preferência será dada ao mouse. Dessa forma, descreve como minimizar a quantidade de código para acelerar e simplificar o processo de desenvolvimento.

Aviso Legal:
, . . , — .

Uma tarefa mínima típica é a seguinte:

Existem campos de entrada de login e senha e um botão de autorização. É necessário que o botão de autorização mude de estado (isEnable) dependendo do que está contido nos campos de entrada.

Uma versão um pouco mais avançada dessa tarefa é semelhante à seguinte:

Temos os campos de entrada de email, senha e número de telefone, além de dois botões - registro e solicitação de um código SMS para o telefone inserido. O botão de registro deve estar disponível apenas quando os dados corretos forem inseridos em cada um dos campos. E o botão de solicitação de código é quando o campo do número de telefone é válido.

Solução típica- criação de sinalizadores interdependentes combinando as instruções "if" e "switch" em um único controlador de exibição. A dificuldade aumentará com o número de controles envolvidos no processo. Uma solução muito mais avançada seria criar uma máquina de estado. A solução é excelente - mas demorada, além disso, com um alto limite de entrada - e isso não é de forma alguma o que eu quero implementar para o desenvolvedor preguiçoso (AKA "verdadeiro").

Lema sobre um desenvolvedor preguiçoso
, , . (« » ), , .

A ideia geral do proposto é a seguinte:

Há uma classe para o campo de entrada e uma classe para o gerente de validação. O campo de entrada é herdado, como esperado, de UITextField. O gerenciador de validação - herdado do UIControl, contém uma coleção de elementos validados (não é necessário descender o UITextField) e implementa o padrão de "observador". Além disso, ele atua como gerente de outros controles que devem alterar seu status de acessibilidade quando o estado dos elementos validados é alterado. Em outras palavras, se o campo contiver um email inválido, o botão de registro não deverá estar disponível.



Os campos de entrada são auto-validados - eles podem responder à pergunta se os dados contidos neles são válidos e mudar seu estado. Alguns desenvolvedores preferem usar uma classe de validador separada (e seus descendentes) para validar os campos de entrada, mas essa prática torna impossível aproveitar totalmente a portabilidade declarativa e escalável do código. É importante para nós que o campo de entrada ou qualquer outro controle validado suporte uma interface simples do formulário:

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

Para adicionar validação ao seu projeto, você precisa adicionar 4 arquivos do exemplo localizado no repositório GitHub .

  • A classe DSTextField implementa um campo de entrada com uma indicação do processo de validação.
  • A classe DSValidationManager é um observador que fornece a funcionalidade de que precisamos.
  • Extensão StringOptional + Utils - contém apenas um método que usa uma expressão regular para verificar se o texto da linha atual é válido.
  • O UIView + Layer é apenas uma maneira conveniente de adicionar um quadro de uma determinada largura a qualquer descendente do UIView.

A rigor, se você deseja implementar a validação da mesma maneira para qualquer outro controle, provavelmente precisará do DSValidationManager. O resto é usado apenas para sua conveniência.

O processo de validação é mostrado no vídeo (gif).


Como você pode ver, antes do início da entrada, o campo está no estado padrão e a validação ocorre quando os dados são inseridos. Se você deixar o campo antes que a validação seja concluída, uma inscrição será exibida no campo notificando o erro. O botão de registro ficará indisponível até que os dois campos se tornem válidos.

Uma situação semelhante se desenvolve na segunda tela (que fica disponível somente após a ativação do registro). Porém, na segunda tela, a validação de campos e botões ocorre independentemente - o botão de registro fica disponível apenas se todos os campos falharem. Ao mesmo tempo, o botão de envio de SMS já está disponível quando o campo com o número de telefone é válido.

E agora o truque:O projeto inteiro contém apenas uma classe para o controlador de exibição, a mesma que o Xcode criou ao criar o projeto a partir do modelo. Está completamente vazio. Mas mesmo isso não é usado. Isso não é difícil de verificar com a ajuda do expedidor.



Um leitor atento percebeu que, à direita do respondente, há um Objeto da paleta de componentes padrão do Xcode. Para isso, o parâmetro Class é substituído pela classe DSValidationManager . O mesmo truque foi feito para os campos de entrada, com a única diferença de que a classe DSTextField é usada lá .

Agora toda a nossa programação se resume às seguintes etapas simples:

  1. Associe a coleção VerifiedControls do ValidationManager aos campos de entrada.


  2. Associe a coleção managedControls do ValidationManager ao botão que você deseja gerenciar.


  3. Associe os campos de entrada na direção oposta ao ValidationManager, tornando-o um representante.

  4. No campo de entrada, configure uma expressão regular para validação e uma mensagem de erro, além de outras propriedades padrão e personalizadas por meio do gerenciador padrão do Xcode. Em princípio, tudo, exceto a expressão regular, pode ser deixado "como está". Requer apenas o uso do teclado e, em seguida, apenas para copiar e colar a fórmula do tyrnet. Se a mensagem de erro estiver ausente, ela simplesmente não será exibida na tela.


O código DSValidationManager é primitivo feio.

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

Como você pode ver, ele recebe uma notificação de uma coleção e atua em outra.

Um observador torna uma propriedade que nem consideramos neste exemplo. Você pode arrastar a conexão do objeto ValidationManager diretamente para o controlador, adicionar um evento semelhante ao clicar em um botão e processar o estado do gerente diretamente no controlador. Se você realmente quer. Isso pode ser útil se você não apenas bloquear o controle, mas também realizar algum trabalho útil (por exemplo, tocar uma marcha ou mostrar o AlertView).

De todo o código TextEdit, deve-se observar apenas que o campo deve ter o cuidado de definir a propriedade " isValid " corretamente e puxar o método " Verified ()"De seu delegado. No entanto, observe que o campo de entrada não usa uma referência ao objeto do gerenciador, mas uma coleção. Isso permite vincular um campo de entrada a vários gerenciadores.

Consequentemente, o método precisará ser utilizado por cada um dos delegados.

    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()
        }
    }

Agora, se você precisar criar um novo formulário com campos de entrada, não precisará nem definir valores de propriedades personalizadas - será suficiente copiar os atributos de Tempo de Execução necessários em massa e aplicá-los a um novo campo (não se esqueça de substituir apenas o nome da classe).


De um modo geral, o truque usando o "objeto" no cabeçalho do formulário pode ser usado muito mais amplamente do que apenas para validação de campo - ele reage com o MVP ao MVVM sem nenhum Rx. Seu uso mais comum é o compartilhamento de rede e a notificação de alterações no modelo CoreData.

All Articles