SwiftUI : componente Toggle

Qué es Toggle

El componente Toggle permite seleccionar un valor entre activado o desactivado. Es un componente equivalente a UISwitch de UIKit.

Aquí podéis consultar la documentación oficial

Para crear el componente usaremos uno de los siguientes ‘inicializadores’:

@State var isOn: Bool = false

...

Toggle("Option 1", isOn: $isOn)
  1. ‘Inicializador’ simple que nos proporciona una etiqueta de texto a la izquierda del switch.
    @State var isOn: Bool = false
    
    ...
    
    Toggle(isOn: $isOn) {
        VStack {
            Text("This is a")
            Text("switch")
        }
    }
  2. ‘Inicializador’ que permite personalizar completamente la vista que aparecerá a la izquierda del switch

Nota: el parámetro isOn es una variable de estado que usará el componente Toggle para asignar el valor seleccionado. Cuando queremos usar un valor fijo (por ejemplo, por motivos de pruebas) podemos usar la función .constant(), que pertenece al struct Binding, para cumplir con su implementación sin tener que declarar la variable de estado.

Cómo leer datos de un Toggle

El componente Toggle necesita que el valor del parámetro isOn sea una variable de estado de tipo Bool. Esta variable de estado será la encargada de almacenar el valor actual del componente, y se modificará cuando el usuario interactúe con el Toggle.

struct ContentView: View {
    @State var value: Bool = true
        
    var body: some View {
        Group {
            Toggle("Option 1", isOn: $value)
        }
    }
}

Por ejemplo: podemos mostrar un texto con el valor actual cuando la variable value sea mayor que 5.

struct ContentView: View {
    @State var value: Bool = false
        
    var body: some View {
        Group {
            Toggle("Option 1", isOn: $value)
            if value {
                Text("Activated!")
            }
        }.padding()
    }
}

Modificadores comunes para Toggle

El componente Toggle comparte los mismos métodos de personalización que el componente View y pueden ser consultados en el siguiente enlace.

Es muy común usar los siguientes modificadores:

toggleStyle

Permite seleccionar el estilo del Toggle. Para iOS solo existe la opción SwitchToggleStyle.

@State var isOn: Bool = false

...

Toggle("Option 1", isOn: $isOn)
    .toggleStyle(SwitchToggleStyle(tint: .red))

Podemos crearnos nuestro propios estilo implementado el protocolo ToggleStyle.

labelsHidden

Permite ocultar los labels del componente. Es muy útil cuando no queremos mostrar ningún texto, dejándonos posicionar el propio switch a nuestro gusto:

Toggle("Option 1", isOn: $toggle1Value)
    .labelsHidden()

Como crear un ToggleStyle personalizado

Como hemos indicado anteriormente, el modificador toggleStyle permite aplicar un estilo al componente Toggle. Este modificador recibe como parámetro cualquier tipo de dato que implemente el protocolo ToggleStyle, por lo que vamos a ver que es necesario para implementar este protocolo.

El protocolo ToggleStyle tiene la siguiente definición en su documentación oficial:

public protocol ToggleStyle {
    associatedtype Body : View

    func makeBody(configuration: Self.Configuration) -> Self.Body

    typealias Configuration = ToggleStyleConfiguration
}

Esto quiere decir que debemos implementar el método makeBody que tiene como parámetro un ToggleStyleConfiguration, y devolverá un View. El objeto ToggleStyleConfiguration tendrá parámetros que podremos usar para controlar el estado actual del Toggle como, por ejemplo, el parámetro isOn.

El resultado final es que la vista que se muestre al usuario como Toggle será lo que devolvamos en este método, por lo que podemos crear una vista completamente diferente o encapsular la propia vista del Toggle en la que queramos.

Por ejemplo, vamos a añadirle un borde al Toggle que cambiará de color dependiendo de si está activado o no.

public struct BorderToggleStyle: ToggleStyle {
    public var tint: Color
    
    public func makeBody(configuration: Configuration) -> some View {
        Group {
            if configuration.isOn {
                Toggle(configuration)
                    .toggleStyle(SwitchToggleStyle(tint: tint))
                    .padding()
                    .overlay(
                        RoundedRectangle(cornerRadius: 10)
                            .stroke(tint, lineWidth: 1)
                    )
            } else {
                Toggle(configuration)
                    .toggleStyle(SwitchToggleStyle(tint: tint))
                    .padding()
                    .overlay(
                        RoundedRectangle(cornerRadius: 10)
                            .stroke(Color.gray, lineWidth: 1)
                    )
            }
        }
    }
}

Esta clase que implementa el protocolo ToggleStyle nos permite controlar si está activo o no a través de la propiedad configuration.isOn. De esta forma podemos devolver una vista de Toggle con el borde de diferente color para conseguir el efecto deseado.

Para usar el estilo BorderToggleStyle solo tendremos que ‘setearlo’ en el propio Toggle:

struct ContentView: View {
    @State var value: Bool = true
    
    var body: some View {
        Group {
            Toggle("Option 3", isOn: $value)
                .toggleStyle(BorderToggleStyle(tint: .blue))
        }
    }
}

También podemos conseguir un estilo parecido a un checkbox de una manera muy sencilla:

public struct CheckToggleStyle: ToggleStyle {
    public func makeBody(configuration: Configuration) -> some View {
        HStack() {
            configuration.label
                .frame(maxWidth: .infinity, alignment: .leading)
            Button(action: {
                configuration.isOn.toggle()
            }, label: {
                Group {
                    if configuration.isOn {
                        Image("check-box-on")
                            .resizable()
                            .scaledToFit()
                    } else {
                        Image("check-box-off")
                            .resizable()
                            .scaledToFit()
                    }
                }
                .frame(width: 25, height: 25)
                
            })
            .frame(width: 44, height: 44)
        }
    }
}

En este caso también estamos usando la propiedad configuration.label que contiene la vista de texto que aparece a la izquierda del componente Toggle.

También estamos creando un botón que se comportará como un Toggle modificando el parámetro configuration.isOn entre true y false, y controlando que imagen se debe mostrar.

Hemos usado las siguientes imágenes para este ejemplo:

Ejemplo

Estos son algunos ejemplos de personalización que podemos llegar a montar en cualquier tipo de vista personalizada que funcione como un Toggle y sea de esta forma.

Puedes encontrar este ejemplo en github.com bajo el apartado Toggle.

Rafael Fernández,
iOS Tech Lider