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:
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) } }
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:
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) } }
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í.