Crear modificadores personalizados con ViewModifier

El protocolo ViewModifier en SwiftUI permite la creación de modificadores personalizados para adaptar los controles según nuestras preferencias. Estos modificadores pueden aplicarse a los elementos de la interfaz de usuario de la misma manera que los modificadores predeterminados. Es común encontrarse con la necesidad de utilizar un control personalizado en varias ocasiones dentro de una vista o diferentes vistas. En lugar de duplicarlo, podemos utilizar ViewModifier para centralizar la personalización del control en un único lugar, simplificando así la estructura de nuestra vista en SwiftUI. Cualquier modificación realizada se aplicará automáticamente a todos los controles sin necesidad de esfuerzo adicional.

Creación de un ViewModifier

Para crear tu primer ViewModifier, considera una vista de SwiftUI llamada MyView que muestra tres avatares:

import SwiftUI
struct MyView: View {
var body: some View {
VStack(spacing: 10) {
Circle()
.fill(Color.blue)
.frame(width: 150, height: 150)
.overlay(
Text("AC")
.font(.largeTitle)
.foregroundColor(.white)
)
Circle()
.fill(Color.red)
.frame(width: 150, height: 150)
.overlay(
Text("MC")
.font(.largeTitle)
.foregroundColor(.white)
)
Circle()
.fill(Color.purple)
.frame(width: 150, height: 150)
.overlay(
Text("NL")
.font(.largeTitle)
.foregroundColor(.white)
)
}
}
}
Una vista de SwiftUI mostrando tres avatares

Estos tres elementos están compuestos por un Circle y un Text superpuesto, con características idénticas. Esta es una situación ideal para utilizar ViewModifier. Al finalizar este artículo, habrás creado un modificador que convertirá cualquier Text en un avatar.

Para comenzar, crea una estructura llamada AvatarModifier que adopte el protocolo ViewModifier:

struct AvatarModifier: ViewModifier {
func body(content: Content) -> some View {
}
}

La función body debe devolver una vista y toma el parámetro content, que hace referencia al control al que se aplicará el modificador. Dentro de esta función, agrega lo siguiente:

// 1
Circle()
.fill(Color.blue)
.frame(width: 150, height: 150)
.overlay(
// 2
content
.font(.largeTitle)
.foregroundColor(.white)
)
  1. Agrega un Circle con los mismos modificadores que en la vista MyView.
  2. Reemplaza el Text con content para referenciar al control al que se aplicará el modificador.

Una vez hecho esto, puedes aplicar AvatarModifier a cualquier control utilizando el modificador modifier, por ejemplo:

Text("MC")
.modifier(AvatarModifier())

Actualiza la vista MyView utilizando AvatarModifier de la siguiente manera:

import SwiftUI
struct MyView: View {
var body: some View {
VStack(spacing: 20) {
// 1
Text("AC")
.modifier(AvatarModifier())
Text("MC")
.modifier(AvatarModifier())
Text("NL")
.modifier(AvatarModifier())
}
}
}
  1. Cada text utiliza el modificador AvatarModifier.
Una vista de SwiftUI mostrando tres avatares utilizando el modificador AvatarModifier

Ahora, la vista es más sencilla, ya que la personalización del control se ha centralizado en un modificador. Además, puedes utilizar el modificador de manera similar a los modificadores predeterminados de SwiftUI extendiendo el protocolo View:

// 1
extension View {
// 2
func avatar() -> some View {
// 3
modifier(AvatarModifier())
}
}
  1. Se extiende del protocolo View.
  2. Se crea una función llamada avatar que devuelve una vista.
  3. Devuelve el modificador AvatarModifier.

Reemplaza .modifier(AvatarModifier()) por .avatar() en MyView.

Agregar propiedades

Una diferencia notable entre la vista actual y la inicial es que cada fondo del avatar tiene un color fijo. Para permitir diferentes colores de fondo, es necesario agregar propiedades al AvatarModifier. En AvatarModifier, agrega la siguiente propiedad encima de la función body:

let backgroundColor: Color

Luego, modifica .fill(Color.blue) por .fill(backgroundColor). Asimismo, actualiza la función avatar en la extensión View para que acepte el parámetro backgroundColor:

extension View {
// 1
func avatar(backgroundColor: Color) -> some View {
modifier(
// 2
AvatarModifier(backgroundColor: backgroundColor)
)
}
}
  1. Se añade el parámetro backgroundColor.
  2. Se utiliza backgroundColor en el modificador AvatarModifier.

Finalmente, en la vista MyView, reemplaza el uso de .avatar() por .avatar(backgroundColor: TU_COLOR), por ejemplo:

Text("AC")
.avatar(backgroundColor: .blue)
Una vista de SwiftUI mostrando tres avatares utilizando el modificador avatar con un color de fondo diferente

Gracias al uso de propiedades, puedes personalizar tu modificador con diferentes configuraciones sin esfuerzo adicional. ¿Te gustaría agregar más propiedades a AvatarModifier? Puedes incluir propiedades para personalizar el tamaño del Circle o el tipo y color de fuente del content.

Agregar variables de estado

Una ventaja de los modificadores personalizados es que puedes agregar variables de estado, igual que en cualquier otra vista de SwiftUI. Para demostrarlo, vamos a agregar una variable de estado booleana a AvatarModifier. Cada vez que se presione un avatar, cambiará su estado para producir una animación sencilla.

Encima de la propiedad backgroundColor, agrega la variable de estado booleana isTapped con el valor predeterminado false:

@State private var isTapped = false

Después del modificador .overlay del Circle, agrega lo siguiente:

// 1
.scaleEffect(isTapped ? 0.9 : 1.0)
// 2
.onTapGesture {
withAnimation {
isTapped.toggle()
}
}
  1. Agrega el modificador .scaleEffect para aplicar una escala diferente según el valor de isTapped.
  2. Agrega el modificador .onTapGesture para detectar cuando se presiona el avatar y cambiar el valor de isTapped con animación.

Al ejecutar el código y presionar en los avatares, observarás cómo se reproduce la animación:

Vista de SwiftUI mostrando tres avatares utilizando el modificador avatar con diferentes colores de fondo y animación al ser presionados

Código completo

import SwiftUI
struct MyView: View {
var body: some View {
VStack(spacing: 20) {
Text("AC")
.avatar(backgroundColor: .blue)
Text("MC")
.avatar(backgroundColor: .red)
Text("NL")
.avatar(backgroundColor: .purple)
}
}
}
struct AvatarModifier: ViewModifier {
@State private var isTapped = false
let backgroundColor: Color
func body(content: Content) -> some View {
Circle()
.fill(backgroundColor)
.frame(width: 150, height: 150)
.overlay(
content
.font(.largeTitle)
.foregroundColor(.white)
)
.scaleEffect(isTapped ? 0.9 : 1.0)
.onTapGesture {
withAnimation {
isTapped.toggle()
}
}
}
}
extension View {
func avatar(backgroundColor: Color) -> some View {
modifier(
AvatarModifier(backgroundColor: backgroundColor)
)
}
}

Comparte este artículo

Subscríbete a nuestro Newsletter

Mantente al día en el mundo de las aplicaciones móviles con nuestro blog especializado.

Artículos semanales

Todas las semanas artículos nuevos sobre el mundo de las aplicaciones móviles.

No spam

No te enviaremos spam, solo contenido de calidad. Puedes darte de baja cuando quieras.

Contenido de calidad

Nada de contenido generado de manera automática usando ChatGPT.

Recomendaciones

Tips indispensables sobre mejores prácticas y metodologías.

© 2024 AsyncLearn