Existem muitas formas de organizar e reutilizar alguns recursos relacionados ao layout. A idéia é abordar um pouco sobre como organizar cores, fontes e estilos para centralizar essas informações e facilitar a manutenção do código.

Quando o Designer do projeto cria a identidade visual do aplicativo, o desenvolvedor vai encontrar certas estruturas que se repetem ao decorrer das telas. O aplicativo tem certos padrões de cores, fontes, botões, menus, entre outros componentes que harmonizam entre si para criar esse conceito visual.

Imagine o quão desgastante, repetitivo e desnecessário seria se toda vez que o desenvolvedor fosse criar o botão de confirmar, ele tivesse que codificar as mesmas linhas de código em toda tela. Mais do que isso, pense o quão longo seria o processo de passar em todas as telas alterando o RGB manualmente da cor principal do aplicativo porque a empresa mudou a cor verde da sua marca para roxo.

A idéia é mostrar como podemos aplicar alguns recursos de codificação que são simples mas agregam muito valor ao seu código. Nem sempre só recursos complexos e mirabolantes fazem o seu código melhor, mais claro e mais fácil de dar manutenção. Em seguida, vamos abordar um pouco sobre UIColor, UIFont e Style.

UIColor

A UIColor é uma classe já bem conhecida que armazena dados de cores e as vezes opacidade. Ela provém algumas cores padrões do sistema e para utilizar cores customizadas ela oferece a possiblidade de construir uma cor a partir do código RGB.

UIColor(red: 51.0 / 255.0, green: 169.0 / 255.0, blue: 124.0 / 255.0, alpha: 1.0)

Existem alguns motivos para defender a organização, nomeação e centralização das cores utilizadas no desenvolvimento do aplicativo:

  • A leitura do código fica mais clara se encontrarmos escrito verde ao invés de códigos RGB.
  • Se a cor mudar, a manutenção fica mais direta e fácil se a definição da cor estiver em um único lugar.
  • Um novo desenvolvedor chegando no projeto se adapta mais rápido quando certos padrões já foram definidos.

Antes de apresentar os códigos na prática, vamos abordar uma funcionalidade bem interessante que o Zeplin disponibiliza. Quando o desenvolvedor criar o seu arquivo de cores, ele pode optar por definir os próprios nomes ou utilizar o recurso automático do Zeplin.

Quando o Designer disponibiliza uma tela no Zeplin, os componentes da tela possuem suas propriedades e neste caso vamos focar na cor. Observe as imagens a seguir. A imagem da esquerda mostra que a cor está definida em RGB e possui do lado um botão com duas gotas. Ao clicar nesse botão o Zeplin define um nome para está cor e a partir desse momento todas as telas que apresentarem essa mesma cor reconhecerão esse nome como padrão.

Mais do que o nome, o Zeplin centraliza no styleguide do projeto todas as cores utilizadas e seus respectivos códigos. Ou seja, o desenvolvedor facilmente copia o trecho do código referente a nova cor e agrega ao seu arquivo de cores.

Depois dessa breve explicação sobre o recurso do Zeplin, vamos voltar para o projeto em Swift. Como sugestão, o arquivo de cores pode ser chamado de UIColor+Zeplin, mas isso fica a critério do desenvolvedor. A idéia é criar uma extension de UIColor e definir todas as cores utilizadas no aplicativo, assim ao decorrer do código o desenvolvedor apenas utilizará a cor através do nome definido pelo próprio Zeplin e em casos de alteração, basta alterar nesta definição que a mudança será aplicado em todo o projeto.

import UIKit

public extension UIColor {
    @nonobjc class var slateGrey: UIColor {
        return UIColor(red: 91.0 / 255.0, green: 99.0 / 255.0, blue: 110.0 / 255.0, alpha: 1.0)
    }
    
    @nonobjc class var seaweedGreen: UIColor {
        return UIColor(red: 51.0 / 255.0, green: 169.0 / 255.0, blue: 124.0 / 255.0, alpha: 1.0)
    }
        
    @nonobjc class var seafoamBlue: UIColor {
        return UIColor(red: 113.0 / 255.0, green: 197.0 / 255.0, blue: 165.0 / 255.0, alpha: 1.0)
    }
    
    @nonobjc class var blueGrey: UIColor {
    	return UIColor(red: 143.0 / 255.0, green: 145.0 / 255.0, blue: 150.0 / 255.0, alpha: 1.0)
    }
}

A partir desse momento, as cores customizadas são facilmente utilizadas como as cores padrões do sistema:

barButtonItem.tintColor = .seafoamBlue
self.currentPageIndicatorTintColor = .seaweedGreen
button.setTitleColor(.seaweedGreen, for: .normal)

UIFont

A classe UIFont disponibiliza algumas fontes do sistema com seus diferentes pesos, mas as vezes o Designer cria a identidade visual do aplicativo utilizando outras fontes que não são as padrões do sistema. Então vamos abordar a seguir uma forma de adicionar no projeto as fontes customizadas.

Para utilizar uma fonte customizada no código é necessário adicionar os arquivos de fonte no projeto e referencia-los no info.plist.

O próximo passo é criar um arquivo, nomeado UIFont+Oswald como sugestão, para fazer uma extension da class UIFont, tornando o uso da fonte customizada mais claro e direto.

import UIKit

public extension UIFont {
    enum Oswald {
        static func medium(size: CGFloat) -> UIFont {
            return UIFont(name: "Oswald-Medium", size: size)!
        }
        static func regular(size: CGFloat) -> UIFont {
            return UIFont(name: "Oswald-Regular", size: size)!
        }

        static func bold(size: CGFloat) -> UIFont {
            return UIFont(name: "Oswald-Bold", size: size)!
        }
        static func extraLight(size: CGFloat) -> UIFont {
            return UIFont(name: "Oswald-ExtraLight", size: size)!
        }

        static func light(size: CGFloat) -> UIFont {
            return UIFont(name: "Oswald-Light", size: size)!
        }
        static func semibold(size: CGFloat) -> UIFont {
            return UIFont(name: "Oswald-SemiBold", size: size)!
        }
    }
}

A partir desse momento, as fonte customizadas são facilmente utilizadas:

button.titleLabel?.font = UIFont.Oswald.medium(size: 20)
button.titleLabel?.font = UIFont.Oswald.regular(size: 17)

Style

Até agora foi abordado um pouco sobre algumas práticas de organização em relação a utilização das classes UIColor e UIFont. Essas praticas unidas à criação de estilos, que vamos abordar a seguir, tornam o desenvolvimento de layouts elegante, interessante e com fácil manutenção. O código fonte é adicionado ao projeto de forma manual e os arquivos podem ser baixados aqui. Gostaria de deixar claro que não encontramos informações sobre a autoria do código fonte.

Após adicionar os aquivos no projeto, o desenvolvedor será capaz de criar estilos para UIButton, UILabel, UIView e muitos outros componentes de layout, tornando o código menos repetitivo e mais fácil de manter. Será exibido a seguir alguns exemplos de como utilizar style, mas a criatividade do desenvolvedor não tem limites pois o leque de possibilidade é muito amplo.

UIButton

Em várias telas de um aplicativo encontramos um botão padrão para confirmar ações. Ele tem cor de fundo, cor de texto, tipo de fonte e tamanho de fonte. O desenvolvedor pode em todas as telas configurar essas propriedades para o botão presente no seu arquivo de layout. Mas, se alguma dessas propriedades alterar no futuro, por uma decisão do Designer ou do cliente, o desenvolvedor terá que revisitar todas as telas e alterar essa informação. Talvez seja simples se for um componente usado em apenas um lugar, mas imagine o custo de alteração de um componente que se repete em várias e várias telas. Neste caso, a centralização com a criação de um estilo torna-se muito interessante.

import UIKit

extension ViewStyle where T == UIButton {
    static var primary: ViewStyle<UIButton> {
        return ViewStyle<UIButton>.init(styler: { button in
            button.backgroundColor = .seaweedGreen
            button.tintColor = .white
            button.setTitleColor(UIColor.white, for: .normal)
            button.titleLabel?.font = UIFont.Oswald.medium(size: 20)
        })
    }
}

A partir dessa definição, em todas as telas que você precisar de um botão com as mesmas características, só é necessário uma linha para aplicar o estilo.

@IBOutlet var confirmButton: UIButton!

confirmButton.setTitle("Cadastrar", for: .normal)
confirmButton.apply(style: .primary)

Algo interessante para abordar é a possibilidade de compor estilos. O aplicativo tem o botão exibido acima e tem uma versão desabilitada. Ambos possuem uma sombra padrão e ao invés de replicar esse código nos dois estilos, o desenvolvedor pode criar um estilo para sombra e combinar com os outros dois estilos.

import UIKit

extension ViewStyle where T == UIButton {
    static var shadow: ViewStyle<UIButton> {
        return ViewStyle<UIButton>.init(styler: { button in
            button.layer.shadowOpacity = 1
            button.layer.shadowColor = UIColor.black50.cgColor
            button.layer.shadowOffset = CGSize(width: 0, height: 1.0)
            button.layer.shadowRadius = 1
        })
    }

    static var primary: ViewStyle<UIButton> {
        let style = ViewStyle<UIButton>.init(styler: { button in
            button.backgroundColor = .seaweedGreen
            button.tintColor = .white
            button.setTitleColor(UIColor.white, for: .normal)
            button.titleLabel?.font = UIFont.Oswald.medium(size: 20)
        })
        
        return ViewStyle<UIButton>.compose(style, .shadow)
    }

    static var disabled: ViewStyle<UIButton> {
        let style = ViewStyle<UIButton>.init(styler: { button in
            button.backgroundColor = UIColor.veryLightPinkThree
            button.tintColor = UIColor.purpleyGrey
            button.setTitleColor(UIColor.purpleyGrey, for: .normal)
            button.titleLabel?.font = UIFont.Oswald.medium(size: 20)
        })
        
        return ViewStyle<UIButton>.compose(style, .shadow)
    }
}

UILabel

Também é possível criar estilos para UILabel, colocando mais versatilidade ao seu código através da criação de funções que agregam parâmetros ao estilo, fazendo dele mais flexível.

Um desenvolvedor quer que todos os empty states do seu aplicativo tenham o mesmo padrão de apresentação, então ele é capaz de definir as propriedades da UILabel e utilizar como mostrado no exemplo dos botões.

import UIKit

extension ViewStyle where T == UILabel {
    static var empty: ViewStyle<UILabel> {
        return ViewStyle<UILabel> {
            $0.textAlignment = .center
            $0.numberOfLines = 0
            $0.textColor = .charcoalGrey
            $0.font = UIFont.systemFont(ofSize: 14)
        }
    }
}
let label = UILabel()
label.apply(style: .empty)

Os estilos podem ter mais do que características relacionadas a tamanho de fonte e cor. Essas informações podem inclusive ser enviadas por parâmetros, deixando o estilo mais genérico e reutilizável. O desenvolvedor pode decidir que ele quer padronizar todos headers presentes em suas tabelas em relação ao estilo da fonte, cor e alinhamento, mas ao mesmo tempo ele percebe que o tamanho da fonte pode ser variável. No exemplo a seguir, uma biblioteca para trabalhar com constraints é utilizada, PureLayout, mas o foco não é a abordagem desta biblioteca. Então, tenha em mente que ela será exibida no código como exemplo das várias possibilidades que o estilo proporciona, mas não será abordada detalhadamente neste contexto.

import UIKit
import PureLayout

extension ViewStyle where T == UILabel {
    static func tableHeader(superview: UIView, fontSize: CGFloat = 21.0) -> ViewStyle<UILabel> {
        return ViewStyle<UILabel> { [unowned superview] in
            $0.font = UIFont.Oswald.bold(size: fontSize)
            $0.textColor = UIColor.charcoalGrey
            $0.adjustsFontSizeToFitWidth = true
            $0.minimumScaleFactor = 0.5
            superview.addSubview($0)
            $0.autoPinEdge(.leading, to: .leading, of: superview, withOffset: 12)
            $0.autoPinEdge(.trailing, to: .trailing, of: superview, withOffset: -12)
            $0.autoPinEdge(.top, to: .top, of: superview, withOffset: 0)
            $0.autoPinEdge(.bottom, to: .bottom, of: superview, withOffset: 8)
        }
    }
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
	let view = UIView()
    let label = UILabel()
    label.apply(style: .tableHeader(superview: view, fontSize: 18))
    label.text = self.sectionTitle
    view.backgroundColor = .clear
    return view
}

UIView

Existem infinitas possiblidade de como o desenvolvedor pode combinar estilos e aplicá-los dentro de outros estilos para criar uma forma agradável de reutilizar estruturas e padronizar o desenvolvimento do layout. Seria impossível abordar todas essas possibilidades nesse artigo, então será exibido mais um exemplo de como utilizar o estilo para criação de um card. Depois fica por conta da criativa dos desenvolvedores combinarem os diversos recursos.

extension ViewStyle {
    static func cornerRadius(_ radius: CGFloat) -> ViewStyle<UIView> {
        return ViewStyle<UIView> {
            $0.layer.masksToBounds = true
            $0.layer.cornerRadius = radius
        }
    }
    
    static func border(color: UIColor, width: CGFloat) -> ViewStyle<UIView> {
        return ViewStyle<UIView> {
            $0.layer.borderColor = color.cgColor
            $0.layer.borderWidth = width
        }
    }
    
    static var card: ViewStyle<UIView> {
        return ViewStyle<UIView>(styler: {
            $0.apply(style: .cornerRadius(4.0))
            $0.apply(style: .border(color: UIColor.veryLightPinkThree, width: 1.0))
        })
    }

}
(blurView as UIView).apply(style: .cornerRadius(4.0))
(noticeView as UIView).apply(style: .card)

Conclusão

Organizando as cores, as fontes e criando estilos para os componentes de um layout, o desenvolvedor consegue tornar o seu código mais legível, mas organizado e muito mais fácil de receber manutenção.