paint-brush
Um guia essencial para SwiftUI: UI reutilizável com modificadores personalizadospor@nemi
1,615 leituras
1,615 leituras

Um guia essencial para SwiftUI: UI reutilizável com modificadores personalizados

por Nemi Shah9m2023/04/28
Read on Terminal Reader

Muito longo; Para ler

Os ViewModifiers personalizados no SwiftUI permitem criar estilos reutilizáveis que podem ser aplicados a todas as suas visualizações. 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. O objetivo deste artigo é cobrir algumas das diferentes maneiras de criar modificadores e estilos personalizados no Swift UI.
featured image - Um guia essencial para SwiftUI: UI reutilizável com modificadores personalizados
Nemi Shah HackerNoon profile picture
0-item
1-item

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:

  • Imagem: Um componente de imagem padrão com algum raio de canto
  • Textos: Temos um título e um corpo de texto
  • Botão: Um botão de largura total

Código SwiftUI simples

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


Há alguns problemas com essa abordagem:

  • 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:

  • Contêiner de tela: a tela em si possui preenchimento universal, isso é opcional, mas prefiro que todas as telas tenham um estilo universal
  • Raio de canto
  • Textos do título e do corpo
  • Largura total: os itens precisam preencher a largura de seu pai (o botão e a imagem no exemplo acima)
  • Estilo do botão
  • Escala de imagem

Modificadores de exibição personalizados

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


Agora vamos converter o código para usá-los em vez de estilizar diretamente:


 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.