es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo cambiar el estado de un Toggle de SwiftUI externamente

Estoy intentando cambiar el estado de un Toggle desde fuera de la vista. He intentado utilizar ObservableObject y EnvironmentObject, pero el toggle espera un Binding (@State).

  • Necesito ejecutar una devolución de llamada cuando el usuario toque el toggle.
  • Necesito cambiar el estado del toggle programáticamente sin ejecutar la devolución de llamada.
  • Estoy usando un modelo compartido para esto y otras vistas, idealmente me gustaría poder usar eso para un Bool ‘enabled’ en lugar de la variable de estado isOn.

Este código me permite ejecutar la devolución de llamada a través de la extensión, pero no puedo averiguar cómo cambiar la variable de estado isOn externamente, y si pudiera hacerlo, supongo que mi devolución de llamada se ejecutaría, lo cual no quiero que suceda.

import SwiftUI

struct ControlView: View {
    var title: String
    var panel: Int
    var callback: () -> Void
    @State public var isOn = false // toggle state
    @EnvironmentObject var state: MainViewModel

    var body: some View {
        VStack() {

            // -- Header
            HStack() {
                Text(" ")
                Image(self.state.panelIcon(panel: panel)).resizable().frame(width: 13.0, height: 13.0)
                Text(title)
                Spacer()
            }.padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
            .background(Color(red: 0.9, green: 0.9, blue: 0.9))

            // -- Switch

            Toggle(isOn: $isOn.didSet { (state) in
                // Activar ARC
                callback()
            }) {
                Text("Habilitar ARC")
            }.padding(EdgeInsets(top: 0, leading: 12, bottom: 10, trailing: 12))

        }.overlay(
            RoundedRectangle(cornerRadius: 10)
                .stroke(Color(red: 0.8, green: 0.8, blue: 0.8), lineWidth: 1.25)
        ).background(Color.white)
    }
}

extension Binding {
    func didSet(execute: @escaping (Value) -> Void) -> Binding {
        return Binding(
            get: { self.wrappedValue },
            set: {
                self.wrappedValue = $0
                execute($0)
            }
        )
    }
}

(Note: The code and output provided are not in markdown format)

Tags:  

Answer

  1. Avatar for davy.ai

    Para cambiar el estado de un Toggle desde fuera de la vista, puedes usar una Binding en lugar de una variable @State. Para ejecutar una devolución de llamada cuando el usuario toca el Toggle, puedes utilizar el modificador didSet en el Binding. Sin embargo, esto también ejecutaría la devolución de llamada cuando la variable de estado cambie programáticamente.

    Para evitar que la devolución de llamada se ejecute cuando el estado cambia programáticamente, puedes usar una variable flag para indicar si el cambio de estado fue iniciado por el usuario o por el código.

    Para utilizar un modelo compartido para un Bool ‘enabled’ en lugar de la variable @State isOn, puedes pasar la variable enabled como parámetro a ControlView y usarla para el Binding.

    Aquí tienes un ejemplo de fragmento de código con estos cambios:

    import SwiftUI
    
    struct ControlView: View {
        var title: String
        var panel: Int
        var callback: () -> Void
        @Binding var enabled: Bool // utilizar modelo compartido para la bandera enabled
        @EnvironmentObject var state: MainViewModel
    
        // bandera para indicar si el usuario activó o desactivó el interruptor
        var userToggled: Bool = false
    
        var body: some View {
            VStack {
                HStack {
                    Text(" ")
                    Image(self.state.panelIcon(panel: panel)).resizable().frame(width: 13.0, height: 13.0)
                    Text(title)
                    Spacer()
                }.padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
                .background(Color(red: 0.9, green: 0.9, blue: 0.9))
    
                Toggle(isOn: $enabled.didSet { state in
                    // ejecutar devolución de llamada solo si el usuario activó o desactivó el interruptor
                    if userToggled {
                        callback()
                    }
                    // reiniciar bandera
                    userToggled = false
                }) {
                    Text("Habilitar ARC")
                }.padding(EdgeInsets(top: 0, leading: 12, bottom: 10, trailing: 12))
                .onTapGesture {
                    // establecer bandera para indicar que el usuario activó o desactivó el interruptor
                    userToggled = true
                }
            }.overlay(
                RoundedRectangle(cornerRadius: 10)
                    .stroke(Color(red: 0.8, green: 0.8, blue: 0.8), lineWidth: 1.25)
            ).background(Color.white)
        }
    }
    

    Para utilizar esta vista, puedes pasar la bandera enabled como un Binding a ControlView:

    @State var enabled = false
    var body: some View {
        ControlView(title: "Control", panel: 1, callback: {
            // función de devolución de llamada
        }, enabled: $enabled)
    }
    

    Y puedes cambiar el estado del toggle programáticamente cambiando el valor de la variable enabled:

    enabled = true // activar el toggle programáticamente
    

Comments are closed.