A capacidade de criar modificadores de exibição personalizados é um recurso poderoso no SwiftUI. Neste artigo, abordaremos exemplos de como esse recurso pode ser usado para tornar a construção da interface do usuário muito mais fácil. Se você não estiver familiarizado com ViewModifiers no SwiftUI e como criar customizados, você pode ler sobre eles aqui
O objetivo deste artigo é cobrir algumas das diferentes maneiras de criar modificadores e estilos personalizados no SwiftUI e como eles podem ser usados para tornar a construção da interface do usuário mais declarativa e, ao mesmo tempo, obter uma saída final limpa e consistente.
A IU final que queremos construir é esta:
Vamos considerar todos os componentes individuais na tela:
Se você construir esta tela sem nenhum modificador, o código ficaria mais ou menos assim:
struct ContentView: View { var body: some View { VStack (alignment: .leading) { Image("feature") .resizable() .aspectRatio(contentMode: .fill) .frame(minWidth: 0, maxWidth: .infinity) .frame(height: 220) .cornerRadius(12) .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI are the best!") .foregroundColor(Color("titleTextColor")) .font(.system(size: 20, weight: .bold)) .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI let you create resuable styles that can be applied to all your views") .foregroundColor(Color("bodyTextColor")) .font(.system(size: 14, weight: .medium)) Spacer() Button(action: { }) { Text("Label") .font(.system(size: 14, weight: .medium)) } .frame(minWidth: 0, maxWidth: .infinity) .padding(.horizontal, 10) .padding(.vertical, 12) .background(Color.blue) .foregroundColor(Color.white) .cornerRadius(12) } .padding(.all, 16) } }
O estilo de alguns dos elementos (o título e os textos de detalhes, por exemplo) teria que ser duplicado
Alterações em alguns dos estilos comuns (preenchimento de elemento, raio de canto, etc.) teriam que ser feitas em vários lugares
Agora, você poderia resolver esse problema da maneira UIKit criando exibições personalizadas, mas não sou fã dessa abordagem porque envolvia afastar-se das exibições integradas e tornar a integração de novos membros da equipe mais friccional. Uma maneira mais fácil seria definir alguns modificadores de exibição universais que podem ser aplicados em vez dos próprios estilos.
Vamos detalhar o estilo comum de que precisamos:
Vamos começar com o raio do canto:
struct CommonCornerRadius: ViewModifier { func body(content: Content) -> some View { content .cornerRadius(12) } }
Este é bastante simples, permite-nos aplicar um raio de canto universal para os elementos. Isso facilita a alteração de estilos de aplicativos globalmente sem a necessidade de criar exibições personalizadas ou fazer várias alterações na base de código.
struct FullWidthModifier: ViewModifier { func body(content: Content) -> some View { content .frame(minWidth: 0, maxWidth: .infinity) } }
Isso facilita a implementação de exibições de largura total, sem adicionar .frame
manualmente!
struct TitleTextModifier: ViewModifier { func body(content: Content) -> some View { content .foregroundColor(Color("titleTextColor")) .font(.system(size: 20, weight: .bold)) } } struct BodyTextModifier: ViewModifier { func body(content: Content) -> some View { content .foregroundColor(Color("bodyTextColor")) .font(.system(size: 14, weight: .medium)) } }
Isso permitirá um estilo de texto comum, normalmente você criaria componentes de texto personalizados ou funções utilitárias e adicionaria componentes de interface do usuário por meio do código.
extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }
Tudo bem, você me entendeu... isso não é um modificador de exibição personalizado, mas uma extensão simples. Isso ocorre porque os ViewModifiers se aplicam às Views genéricas e algumas funções, como resizable
, aplicam-se apenas a imagens. O uso de uma combinação de extensões e modificadores personalizados ajuda a contornar isso.
struct FullWidthButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .fullWidth() .foregroundColor(Color.white) .font(.system(size: 14, weight: .medium)) .padding(.horizontal, 10) .padding(.vertical, 12) .background(configuration.isPressed ? Color.blue.opacity(0.2) : Color.blue) } } struct FullWidthButton: ViewModifier { func body(content: Content) -> some View { content .buttonStyle(FullWidthButtonStyle()) } }
Por fim, isso é para o botão, observe que, embora pudéssemos simplesmente criar um ViewModifier para obter o mesmo efeito, a aparência do botão não seria alterada quando tocada. Isso ocorre porque definir .background
em um botão o força a usar esse plano de fundo nos estados tocado e não tocado. ButtonStyle
nos permite alterar a opacidade do botão com base no fato de ser pressionado ou não.
Agora, por conveniência, gosto de fazer extensões que usam esses modificadores:
extension View { func commonCornerRadius() -> some View { modifier(CommonCornerRadius()) } func fullWidth() -> some View { modifier(FullWidthModifier()) } func title() -> some View { modifier(TitleTextModifier()) } func body() -> some View { modifier(BodyTextModifier()) } func fullWidthButton() -> some View { modifier(FullWidthButton()) } } extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }
struct ContentView: View { var body: some View { VStack (alignment: .leading) { Image("feature") .aspectFill() .fullWidth() .frame(height: 220) .commonCornerRadius() .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI are the best!") .title() .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI let you create resuable styles that can be applied to all your views") .body() Spacer() Button(action: { }) { Text("Awesome") } .fullWidthButton() .commonCornerRadius() } .padding(.all, 16) } }
Muito mais limpo! Agora, à primeira vista, isso parece mais código e esforço do que simplesmente definir manualmente os estilos, mas a longo prazo, isso economizará muito esforço. Pessoalmente, essa abordagem também incentiva o estilo do seu aplicativo a ser mais consistente, contando mais com modificadores comuns do que com uma base de estilo de exibição por exibição.
E é sobre isso! Esperançosamente, isso ajuda você a criar seus aplicativos com mais rapidez e facilidade. Outro benefício é que esses modificadores podem ser inseridos em qualquer um dos seus aplicativos e ajustados para corresponder às diretrizes de estilo. Eu também tenho trabalhado em uma biblioteca para levar isso ainda mais longe, você pode conferir aqui (PS: No momento em que escrevo isso, a biblioteca está em um estágio super inicial, o repositório está vazio :p, mas fique atento)
Também publicado aqui.