BoxView - pagamento automático conveniente para iOS

Gostaria de compartilhar uma biblioteca para criar com eficiência uma interface do usuário para aplicativos iOS com base no layout automático.



Embora com o advento do SwiftUI, a relevância do autolayout esteja diminuindo rapidamente, enquanto esse mecanismo ainda é usado ativamente, e a biblioteca pode ser útil para quem cria (ou altera) a interface do usuário diretamente no código.



Essa maneira de construir uma interface tem várias desvantagens que limitam seu uso:



  • A criação dos elementos NSLayoutConstraint é muito inconveniente.
  • Má visibilidade - é difícil entender o código para entender a aparência da interface do usuário.
  • Um grande número de código de rotina. Para colocar cada visualização, é necessária uma média de cerca de 3 restrições, ou seja, três linhas do mesmo código.
  • A complexidade da criação de interfaces que mudam dinamicamente: você precisa salvar restrições em variáveis ​​separadas para poder alterá-las e, muitas vezes, criar restrições redundantes e desativar restrições desnecessárias.


O primeiro problema pode ser facilmente resolvido envolvendo os métodos padrão para criar restrições em algo mais humano. E isso já está bem implementado, por exemplo, no SnapKit , TinyConstraints e outras bibliotecas semelhantes.



Mas, ainda assim, você deve escrever muito do mesmo tipo de código e há problemas com a visibilidade e as alterações dinâmicas do layout. O UIStackView resolve esses problemas de maneira inteligente, mas, infelizmente, o UIStackView possui uma personalização muito limitada da posição de elementos individuais. Portanto, surgiu a ideia de um UIView de contêiner que controla o layout da pilha de suas subvisões, mas com a capacidade de personalizar individualmente o local de cada subview.

Essa é a abordagem por trás do BoxView e provou ser muito eficaz. O BoxView permite eliminar quase completamente a criação manual de restrições, quase toda a interface do usuário é formada como um sistema de BoxViews aninhados. Como resultado, o código ficou muito mais curto e claro, os benefícios são especialmente visíveis para as UIs dinâmicas.



O BoxView é, de várias maneiras, semelhante ao UIStackView padrão, mas usa regras diferentes para colocar uma subvisão: é possível definir recuos e tamanhos para cada subvisão individualmente. Para criar um layout, o BoxView usa uma matriz de BoxItems, que contém todas as visualizações que precisam ser exibidas e informações sobre como organizá-las. E isso não exige muito código - a maioria dos parâmetros de layout é tomada por padrão e apenas os valores necessários são especificados explicitamente.



A propriedade essencial do BoxView é que ele cria apenas as restrições especificadas para as subvisões adicionadas e nada mais. Portanto, ele pode ser usado sem restrições em conjunto com outras bibliotecas e métodos de layout.



Como exemplo, considere criar um login de formulário simples usando o BoxView (o código de exemplo completo com uma descrição passo a passo está disponível no projeto BoxViewExample no github ).



imagem


Algumas linhas de código são suficientes para criar esse layout no BoxView:



        nameBoxView.items = [nameImageView.boxed.centerY(), nameField.boxed]
        passwordBoxView.items = [passwordImageView.boxed.centerY(), passwordField.boxed]
        boxView.insets = .all(16.0)
        boxView.spacing = 20.0
        boxView.items = [
            titleLabel.boxed.centerX(padding: 30.0).bottom(20.0),
            nameBoxView.boxed,
            passwordBoxView.boxed,
            forgotButton.boxed.left(>=0.0),
            loginButton.boxed.top(30.0).left(50.0).right(50.0),
        ]


O elemento BoxItem é criado a partir de qualquer UIView usando a variável in a box, após o qual pode ser definido como preenchimento em 4 lados, alinhamento e tamanhos absolutos ou relativos.



Todos os elementos do layout podem ser adicionados e removidos livremente (inclusive com animação) sem afetar o posicionamento do restante. Como exemplo, adicionamos uma verificação de campos de entrada vazios e, em caso de erro, mostraremos a mensagem diretamente sob o campo vazio:



imagem


E embora a mensagem deva se "integrar" ao layout existente, você nem precisa alterar o código existente para isso!



    func showErrorForField(_ field: UITextField) {
        errorLabel.frame = field.convert(field.bounds, to: boxView)
        let item = errorLabel.boxed.top(-boxView.spacing).left(errorLabel.frame.minX - boxView.insets.left)
        boxView.insertItem(item, after: field.superview, z: .back)
        boxView.animateChangesWithDurations(0.3)
    }
    
    @objc func onClickButton(sender: UIButton) {
        for field in [nameField, passwordField] {
            if field.text?.isEmpty ?? true {
                showErrorForField(field)
                return
            }
        }
        // ok, can proceed with login
    }
    
    @objc func onChangeTextField(sender: UITextField) {
        errorLabel.removeFromSuperview()
        boxView.animateChangesWithDurations(0.3)
    }


O BoxView suporta todo o kit de ferramentas de autolayout: distâncias entre elementos, tamanhos absolutos e relativos, prioridades, suporte à linguagem RTL. Além do UIView, você também pode usar objetos invisíveis, UILayoutGuides, como elementos de layout. O layout flexível também pode ser usado. Obviamente, o próprio esquema de layout, na forma de um sistema de pilhas UIView aninhadas, não cobre 100% de todas as opções possíveis para o arranjo relativo de elementos, mas isso não é necessário. É bom para a grande maioria das interfaces de usuário típicas e, para casos mais exóticos, você sempre pode adicionar restrições adicionais apropriadas de qualquer outra maneira. Vários métodos utilitários, por exemplo, para criar restrições de proporção, também estão incluídos na biblioteca.



Outro pequeno exemplodisponível no github (~ 100 linhas de código!) ilustra o uso do sistema BoxView aninhado em conjunto com outros métodos de configuração de restrições, bem como alterações animadas nos parâmetros do BoxView.



imagem


Projeto BoxView no github



All Articles