paint-brush
Una guía esencial para SwiftUI: interfaz de usuario reutilizable con modificadores personalizadospor@nemi
1,615 lecturas
1,615 lecturas

Una guía esencial para SwiftUI: interfaz de usuario reutilizable con modificadores personalizados

por Nemi Shah9m2023/04/28
Read on Terminal Reader

Demasiado Largo; Para Leer

Los ViewModifiers personalizados en SwiftUI le permiten crear estilos reutilizables que se pueden aplicar a todas sus vistas. En este artículo, cubriremos ejemplos de cómo se puede usar esta función para hacer que la creación de la interfaz de usuario sea mucho más fácil. El objetivo de este artículo es cubrir algunas de las diferentes formas de crear modificadores y estilos personalizados en la interfaz de usuario de Swift.
featured image - Una guía esencial para SwiftUI: interfaz de usuario reutilizable con modificadores personalizados
Nemi Shah HackerNoon profile picture
0-item
1-item

La capacidad de crear modificadores de vista personalizados es una característica poderosa en SwiftUI. En este artículo, cubriremos ejemplos de cómo se puede usar esta función para hacer que la construcción de la interfaz de usuario sea mucho más fácil. Si no está familiarizado con ViewModifiers en SwiftUI y cómo crear uno personalizado, puede leer sobre ellos aquí


El objetivo de este artículo es cubrir algunas de las diferentes formas de crear modificadores y estilos personalizados en SwiftUI y cómo se pueden usar para hacer que la interfaz de usuario sea más declarativa y al mismo tiempo lograr un resultado final limpio y consistente.


La interfaz de usuario final que queremos construir es esta:


Consideremos todos los componentes individuales en la pantalla:

  • Imagen: un componente de imagen estándar con algún radio de esquina
  • Textos: Tenemos un título y un cuerpo de texto
  • Botón: un botón de ancho completo

Código SwiftUI simple

Si crea esta pantalla sin ningún modificador, el código se vería así:


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


Hay un par de problemas con este enfoque:

  • El estilo de algunos de los elementos (el título y los textos de detalles, por ejemplo) tendría que duplicarse

  • Los cambios en algunos de los estilos comunes (relleno de elementos, radio de esquina, etc.) deberían realizarse en varios lugares


Ahora podría resolver este problema a la manera de UIKit mediante la creación de vistas personalizadas, pero no soy partidario de este enfoque porque implicó alejarse de las vistas integradas y hace que la incorporación de nuevos miembros del equipo sea más conflictiva. Una forma más fácil sería definir algunos modificadores de vista universales que se puedan aplicar en lugar de los estilos mismos.


Analicemos el estilo común que necesitamos:

  • Contenedor de pantalla: la pantalla en sí tiene relleno universal, esto es opcional, pero prefiero que todas las pantallas tengan un estilo universal
  • Radio de esquina
  • Textos de título y cuerpo
  • Ancho completo: los elementos deben poder llenar el ancho de su padre (el botón y la imagen en el ejemplo anterior)
  • Estilo de botón
  • Escalado de imagen

Modificadores de vista personalizados

Comencemos con el radio de la esquina:

 struct CommonCornerRadius: ViewModifier { func body(content: Content) -> some View { content .cornerRadius(12) } }


Este es bastante simple, nos permite aplicar un radio de esquina universal para elementos. Esto hace que sea más fácil cambiar los estilos de la aplicación globalmente sin tener que crear vistas personalizadas o tener que realizar varios cambios en la base de código.


 struct FullWidthModifier: ViewModifier { func body(content: Content) -> some View { content .frame(minWidth: 0, maxWidth: .infinity) } }


Este hace que sea más fácil implementar vistas de ancho completo, ¡no más agregar .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)) } }


Esto permitirá un estilo de texto común, normalmente crearía componentes de texto personalizados o funciones de utilidad y agregaría componentes de interfaz de usuario a través del código.


 extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }


Muy bien, me entendiste... este no es un modificador de vista personalizado sino una extensión simple. Esto se debe a que ViewModifiers se aplica a las vistas genéricas y algunas funciones, como resizable , solo se aplican a las imágenes; el uso de una combinación de extensiones y modificadores personalizados ayuda a evitar esto.


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


Finalmente, esto es para el botón, tenga en cuenta que si bien podríamos haber creado simplemente un ViewModifier para lograr el mismo efecto, la apariencia del botón no habría cambiado cuando se tocó. Esto se debe a que configurar .background en un botón lo obliga a usar ese fondo tanto en estado tocado como no tocado. ButtonStyle nos permite cambiar la opacidad del botón en función de si está presionado o no.


Ahora, por conveniencia, me gusta hacer extensiones que usen estos 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) } }


Ahora vamos a convertir el código para usar estos en lugar de diseñarlos directamente:


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


¡Mucho más limpio! Ahora, a primera vista, esto se siente como más código y esfuerzo que simplemente configurar manualmente los estilos, pero a la larga, esto ahorrará mucho esfuerzo. Personalmente, este enfoque también fomenta que el estilo de su aplicación sea más coherente al confiar más en modificadores comunes que en una base de estilo vista por vista.


¡Y eso es todo! Con suerte, esto lo ayudará a crear sus aplicaciones de manera más rápida y sencilla. Otro beneficio es que estos modificadores se pueden colocar en cualquiera de sus aplicaciones y modificar para que coincidan con sus pautas de estilo. También he estado trabajando en una biblioteca para llevar esto aún más lejos, puedes consultarla aquí (PD: en el momento de escribir esto, la biblioteca se encuentra en una etapa muy temprana, el repositorio está vacío: p, pero estad atentos)


También publicado aquí.