¿Qué es Button?

¿Qué es Button?

El componente Button será usado para mostrar botones a los que el usuario podrá pulsar para desencadenar eventos. Es un componente equivalente a UIButton de UIKit.

Puedes consultar la documentación oficial aquí.

La creación de este componente se puede realizar de varias formas:

Button("Hello, Button 1!") {
        //Button pressed, do something
}

Este ‘inicializador’ tiene un primer parámetro con el texto del botón y un segundo parámetro con el closure que se ejecutará cuando se pulse el botón.

Button(action: {
    //Button pressed, do something
}) {
    VStack {
        Text("Hello")
        Divider()
            .background(Color.red)
            .frame(width: 100, height: 1)
        Text("Button 2!")
    }
}

Este ‘inicializador’ tiene un primer parámetro con el closure que se ejecutará cuando se pulse el botón y un segundo parámetro con un ViewBuilder que nos permite añadir una vista personalizada como botón.

Modificadores comunes para Button

A parte de los modificadores que se explicarán a continuación, el componente Button  comparte los mismos métodos de personalización que el componente View y pueden ser consultados aquí.

buttonStyle

Permite indicar el estilo base del botón. El parámetro pasado debe implementar el protocolo PrimitiveButtonStyle. Por defecto existen las siguientes implementaciones (para iOS):

  • BorderlessButtonStyle. Es un estilo de botón sin bordes y tintado por defecto.
  • DefaultButtonStyle. Este estilo varía dependiendo de la plataforma. En el caso de iOS se aplica el estilo BorderlessButtonStyle.
  • PlainButtonStyle. No aplica estilos por defecto al botón, pero sí efectos para indicar diferentes estados: pressedfocused, o enabled.
Button("Button Borderless") {
    textAlert = "Button Borderless"
    alertPresent.toggle()
}
.buttonStyle(BorderlessButtonStyle())
Button("Button Default") {
    textAlert = "Button Default"
    alertPresent.toggle()
}
.buttonStyle(DefaultButtonStyle())
Button("Button Plain") {
    textAlert = "Button Plain"
    alertPresent.toggle()
}
.buttonStyle(PlainButtonStyle())

background

Permite modificar el color de fondo del botón.

Button("Button with background") {
    //Button pressed, do something
}
.background(Color.yellow)

El color afectará al tamaño que ocupe el propio texto que contenga el botón. Si quisiéramos que ocupara todo el ancho (por ejemplo) tendríamos que asignarle el frame antes de la propiedad background.

Button("Button with background (frame set)") {
    //Button pressed, do something
}
.frame(maxWidth: .infinity)
.background(Color.yellow)

foregroundColor 

Permite modificar el color del texto del botón.

Button("Foreground Color") {
    //Button pressed, do something
}
.foregroundColor(.purple)

hoverEffect

Permite aplicar un estilo de efecto resaltado cuando un puntero pasa sobre el botón.

Button("Button Hover Effect (try on iPad with pointer)") {
    //Button pressed, do something
}
.hoverEffect(.lift)

Para probar esta funcionalidad se debe ejecutar la app en iPad y simular un puntero en en el dispositivo (en el simulador se puede hacer pulsando las teclas  ⌃ + ⌘ + P).

Aumentar el area de pulsación de Button

El componente Button, como cualquier View, tiene modificadores para aumentar su padding o su frame, pero es importante saber que usarlos sobre el Button no afecta al area de pulsación de este si no que es meramente estético.

El área de pulsación de un Button lo define su label, por lo que para aumentar su area de pulsación habría que modificar las propiedades del label para que ocupe más espacio (ya sea con un framepadding o similar).

Button(action: {
    //Button pressed, do something
}, label: {
    Text("Test tappable area")
        .frame(width: 200, height: 200)
})
.background(Color.red.opacity(0.3))

Crear un botón con efectos de pulsación

En SwiftUI los botones permiten capturar los eventos de pulsación para desencadenar eventos, pero no podemos tenemos accesible ninguna propiedad para saber si se está pulsando el botón, y así poder modificar los estilos del propio botón u otras partes de la aplicación.

Esto no quiere decir que sea imposible pero nos obliga a crearnos un ButtonStyle que nos de acceso a esta posibilidad. El protocolo ButtonStyle contiene un objeto de tipo ButtonStyleConfiguration que nos da acceso a la propiedad isPressed con la que podemos controlar si el botón está seleccionado o no. Esto nos indica que para conseguir esta funcionalidad tenemos que crearnos un ButtonStyle personalizado para poder acceder a la variable isPressed.

Lo primero será crearnos el tipo SelectableButtonStyle que implemente el protocolo ButtonStyle.

import SwiftUI

struct SelectableButtonStyle: ButtonStyle {
    private let render: (Self.Configuration) -> T
    
        public init(@ViewBuilder _ render: @escaping (Self.Configuration) -> T) {
        self.render = render
    }
    
    func makeBody(configuration: Self.Configuration) -> some View {
        return self.render(configuration)
    }
}

Una vez creado solo será necesario asignarlo al modificador buttonStyle del botón que queramos y asignar los estilos sobre el closure del init. Para asignar los estilos accederemos a la propiedad label del objeto Configuration que recibimos como parámetro:

Button("Customize on pressed") {
    //Button pressed, do something
}
.padding()
.background(Color.blue)
.buttonStyle(SelectableButtonStyle({
    return $0.label
        .background(Color.yellow)
        .clipShape(RoundedRectangle(cornerRadius: $0.isPressed ? 16.0 : 0.0))
        .overlay(RoundedRectangle(cornerRadius: $0.isPressed ? 16.0 : 0.0).stroke(lineWidth: $0.isPressed ? 2.0 : 0.0).foregroundColor(Color.pink))
                                    .animation(.linear)
}))
Button(action: {
    //Button pressed, do something
}, label: {
    VStack {
        Text("Hello")
        Divider()
            .background(Color.red)
            .frame(width: 100, height: 1)
        Text("Button!")
    }
}).buttonStyle(SelectableButtonStyle({
    return $0.label
        .background(Color.yellow)
        .clipShape(RoundedRectangle(cornerRadius: $0.isPressed ? 16.0 : 0.0))
        .overlay(RoundedRectangle(cornerRadius: $0.isPressed ? 16.0 : 0.0).stroke(lineWidth: $0.isPressed ? 2.0 : 0.0).foregroundColor(Color.pink))
                                    .animation(.linear)
}))

Ejemplo

Puedes encontrar este ejemplo en Github bajo el apartado Button aquí.

Rafael Fernández,
iOS Tech Lider