Los modificadores de los controles, efectos, layout, texto, imagen y demás, son métodos que crean una nueva vista a partir de una existente. Se pueden encadenar tantos modificadores para personalizar una vista como sea necesario, teniendo en cuenta que algunas veces el orden aplicado importa.
A continuación presento algunos modificadores como ejemplo:
Relleno
padding(_:_:) agrega un relleno a ciertos bordes de la vista. Recibe:
-
edges: conjunto de bordes que se van a rellenar. Por defecto es all. -
length: cantidad de puntos que se van a rellenar en un borde específico. Si se pasanil, SwiftUI usa el valor por defecto de la plataforma (i.e. 8).
Text("Hola mundo")
.padding(.leading, 200)
Borde
border(_:width:) añade un borde a la vista con un estilo y grosor específico. Recibe:
-
content: Valor que conforma el protocoloShapeStylecomoColoroHierarchicalShapeStyle, usado para rellenar el borde. Un ejemplo deHierarchicalShapeStyleesLinearGradient. -
width: Grosor del borde. El valor por defecto es 1 pixel.
Text("Hola mundo")
.border(
LinearGradient(
colors: [.red, .blue],
startPoint: UnitPoint(x: 0, y: 0.5),
endPoint: UnitPoint(x: 1, y: 0.5)
),
width: 10
)
// ...
Text("Hola mundo")
.border(Color.systemBlue, width: 10)
Encajar (estirar/repetir) imagen
resizable(capInsets:resizingMode:) define el modo usado por SwiftUI para encajar una imagen dentro de un espacio. Recibe:
-
capInsets: Valores de relleno que indican la porción de la imagen que no se va a encuadrar. -
resizingMode: Modo usado para encuadrar la imagen. Por defecto esImage.ResizingMode.stretch.
Image(systemName: "pencil")
.resizable()
Encuadrar vista
frame(width:height:alignment:) posiciona una vista dentro de un marco invisible con el tamaño especificado. Recibe:
-
width: El ancho de la vista resultante. Siwidthesnil, la vista resultante toma el comportamiento de tamaño de la vista de entrada. -
height: La altura de la vista resultante. Siheightesnil, la vista resultante toma el comportamiento de tamaño de la vista de entrada. -
alignment: Alineación de la vista dentro del recuadro resultante. Aparentemente no tiene ningún efecto si el tamaño de la vista es igual al recuadro. Por defecto escenter.
Image(systemName: "pencil")
.resizable()
.frame(width: 200, height: 200)
Colorear una vista
foregroundStyle(_:) define un estilo para una vista (View) de primer plano. Recibe:
-
style: El color o patrón usado para pintar los elementos de primer plano, que debe conformar el protocoloShapeStyle. Para indicar un valor específico, usarColoroimage(_:sourceRect:scale:), o uno de los tipos de gradientes comolinearGradient(colors:startPoint:endPoint:).
Image(systemName: "pencil")
.resizable()
.foregroundStyle(
.linearGradient(
colors: [.yellow, .blue],
startPoint: .top,
endPoint: .bottom
)
)
Rellenar una forma
fill(_:style:) rellena una forma (Shape) con un color. Recibe:
-
style: Las opciones de estilo que determinan como se rellena la forma.
Path { path in
path.move(to: .zero)
path.addLine(to: CGPoint(x: 50, y: 0))
path.addArc(
center: .zero,
radius: 50,
startAngle: .zero,
endAngle: .degrees(90),
clockwise: false)
}
.fill(.radialGradient(
Gradient(colors: [.yellow, .red]),
center: .topLeading,
startRadius: 15,
endRadius: 80))
Aplicar un color de tinte
tint(_:) sirve para sobrescribir el color de acento predeterminado de una vista. Recibe:
-
tint: Color a aplicar.
Button {
// Answer the call
} label: {
Label("Answer", systemImage: "phone")
}
.tint(.green)
Alerta
alert(_:isPresented:actions:message:) presenta una alerta con un mensaje cuando la condición data es verdadera, usando una vista Text como título. Recibe:
-
title: Título de la alerta. -
isPresented: Un binding a valor booleano que determina si se debe presentar la alerta. Cuando el usuario presiona alguna de las acciones de la alerta, el sistema pone el valor de este binding en false y cierra la alerta. -
actions: UnViewBuilderque retorna las acciones de la alerta. -
message: UnViewBuilderque retorna el mensaje de la alerta.
struct Login: View {
@State private var didFail = false
let alertTitle: String = "Login failed."
var body: some View {
LoginForm(didFail: $didFail)
.alert(
Text(alertTitle),
isPresented: $didFail
) {
Button("OK") {
// Manejar la acción "OK"
}
} message: {
Text("Please check your credentials and try again.")
}
}
}
Sombra
shadow(color:radius:x:y:) agrega una sombra a la vista. Recibe:
-
color: El color de la sombra. Por defecto es negro con opacidad 0.33. -
radius: Determina el cuán difuminada está la sobra. -
x: Desplazamiento horizontal de la sombra con respecto a la vista. Por defecto es 0. -
y: Desplazamiento vertical de la sombra con respecto a la vista. Por defecto es 0.
struct Shadow: View {
let steps = [0, 5, 10]
var body: some View {
VStack(spacing: 50) {
ForEach(steps, id: \.self) { offset in
HStack(spacing: 50) {
ForEach(steps, id: \.self) { radius in
Color.blue
.shadow(
color: .primary,
radius: CGFloat(radius),
x: CGFloat(offset), y: CGFloat(offset))
.overlay {
VStack {
Text("\(radius)")
Text("(\(offset), \(offset))")
}
}
}
}
}
}
}
}
Ignorar el área segura
ignoresSafeArea(_:edges:) expande el área segura de una vista. Recibe:
-
regions: Las regiones sobre las que se expande el área segura de una vista. El modificador se expande en todas las áreas por defecto. -
edges: El conjunto de bordes a expandir. Todo borde que no se incluye permanecerá sin cambios. Por defecto, se incluyen todos los bordes.
ZStack {
Color.red
.ignoresSafeArea()
Text("Hola")
}
Poner una figura de fondo
background(in:fillStyle:) define el fondo de una vista para que sea una figura "insettable" rellena con el estilo "background" por defecto. Recibe:
-
shape: Una instancia del tipoInsettableShapeque se dibuja detrás de la vista usando el estilobackground. -
fillStyle: ElFillStylea usar al dibujar la figura.
ZStack {
Color.teal
Label("Flag", systemImage: "flag.fill")
.padding()
.background(in: RoundedRectangle(cornerRadius: 8))
}
En el siguiente ejemplo, el texto tiene color negro mientras que la cápsula se pinta, pero no tiene color. Por esta razón, está pintando negro sobre negro y no se ve nada. La cápsula solo dibuja su forma, pero no aplica relleno si no se le indica uno.
VStack {
Text("Hola mundo")
.background(Capsule())
}
Para solucionarlo hay que rellenar la cápsula:
Text("Hola mundo")
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(
Capsule().fill(.blue)
)
.foregroundColor(.white)
Pintar con un margen dentro de un recuadro
inset(by:) retorna la misma forma de tipo InsettableShape retraída desde los bordes del rectángulo contenedor, desplazándola hacia adentro en cuanto al dibujo.
Si X es positivo: la forma se reduce (queda más pequeña). Si X es negativo: la forma "se expande" hacia afuera (es decir, se dibuja más grande que el contenedor original).
ZStack {
Capsule()
.fill(Color.blue)
Capsule()
.inset(by: 3)
.fill(Color.red)
// Como .fill retorna un ShapeView y .inset
// es un modificador de InsettableShape, entonces hay error
// si se invierte el orden
}
Orden de los modificadores
Cuando se aplica más de un modificador a una vista, algunas veces el orden importa.
Modificadores como padding y frame cambian la posición o forma de la vista. Modificadores como background o border rellenan o envuelven una vista. Normalmente se quiere definir la forma y posición de una vista antes de rellenarla (fill) o envolverla (wrap).
Text("Hola mundo")
.padding()
.border(Color.purple)
En el ejemplo anterior se envuelve el texto de la vista Text con padding, y luego se pone un borde morado alrededor del texto con relleno.
Si se aplica primero el borde, este rodeará el área intrínseca del texto. Luego, padding agrega cierto relleno alejando a los vecinos.
Text("Hola mundo")
.border(Color.purple)
.padding()
Recordar que usualmente se quiere aplicar los modificadores que afectan la forma y posición de una vista, antes de rellenarla (fill) o envolverla (wrap).
Modificadores de tipos concretos de vistas
Algunos modificadores pueden ser aplicados a todos los tipos de vistas, mientras que otros solo pueden ser aplicados a algunos tipos como Text o Shape.
Por otro lado, no todos los modificadores de Text retornan una vista tipo Text. Por ejemplo, font, fontWeight, bold e italic modifican una vista Text para producir otro Text. En este caso, no importa el orden de los modificadores.
Sin embargo, otros operadores como lineLimit retornan some View, así que el siguiente código arroja un error:
Text(guess.intString)
.lineLimit(0)
.bold()
Mientras que el siguiente funciona correctamente:
Text(guess.intString)
.bold()
.lineLimit(0)
Modificador personalizado
Se puede construir un modificador personalizado agregando una extensión al tipo de dato que se quiere modificar, y se tiene que devolver un tipo de dato específico. Por ejemplo, si se quiere modificar cualquier vista y retornar una vista con tipo opaco, se tiene que crear una extensión de View y retornar some View.
extension View {
func miModificador(radius: CGFloat = 16, offset: CGFloat = 6) -> some View {
return ...
}
}
Modificador de ambiente de nivel-de-vista
En ocasiones, aplicar un modificador a una vista puede afectar a todas sus hijas. Este tipo de modificador se conoce como view-level environment value. Por ejemplo, al aplicar font a un VStack, todas las vistas hijas se ven afectadas, por lo que todos los Text usarían headline como tamaño de fuente:
VStack {
// ...
}
.font(.headline)
Los modificadores de ambiente pueden ser modificados por una vista hija específica. Por ejemplo, en el siguiente ejemplo se puede sobreescribir el tamaño de fuente del Text:
VStack {
Text("Hola mundo")
.font(.subheadline)
Text("Chao mundo")
}
.font(.headline)
Top comments (0)