SwiftUI - el uso de la variable número dentro de un ForEach

0

Pregunta

Tengo una vista creada a través de un bucle ForEach que necesita para tomar una variable de recuento dentro del ForEach sí mismo es decir, necesito la app para reaccionar a una dinámica de conteo y cambio de la interfaz de usuario accoridngly.

Aquí está el punto de vista que estoy tratando de modificar:

struct AnimatedTabSelector: View {
    let buttonDimensions: CGFloat
    @ObservedObject var tabBarViewModel: TabBarViewModel
    
    var body: some View {
        HStack {
            Spacer().frame(maxWidth: .infinity).frame(height: 20)
                .background(Color.red)
            
            ForEach(1..<tabBarViewModel.activeFormIndex + 1) { _ in
                Spacer().frame(maxWidth: buttonDimensions).frame(height: 20)
                    .background(Color.blue)
                Spacer().frame(maxWidth: .infinity).frame(height: 20)
                    .background(Color.green)
            }
            
            Circle().frame(
                width: buttonDimensions,
                height: buttonDimensions)
                .foregroundColor(
                    tabBarViewModel.activeForm.loginFormViewModel.colorScheme
                )
            
            ForEach(1..<tabBarViewModel.loginForms.count - tabBarViewModel.activeFormIndex) { _ in
                Spacer().frame(maxWidth: .infinity).frame(height: 20)
                    .background(Color.red)
                Spacer().frame(maxWidth: buttonDimensions).frame(height: 20)
                    .background(Color.blue)
            }
            
            Spacer().frame(maxWidth: .infinity).frame(height: 20)
                .background(Color.gray)
        }
    }
}

Y el viewModel estoy viendo:

class TabBarViewModel: ObservableObject, TabBarCompatible {
    var loginForms: [LoginForm]
    @Published var activeForm: LoginForm
    @Published var activeFormIndex = 0
    private var cancellables = Set<AnyCancellable>()
    
    init(loginForms: [LoginForm]) {
        self.loginForms = loginForms
        self.activeForm = loginForms[0] /// First form is always active to begin
        setUpPublisher()
    }
    
    func setUpPublisher() {
        for i in 0..<loginForms.count {
            loginForms[i].loginFormViewModel.$isActive.sink { isActive in
                if isActive {
                    self.activeForm = self.loginForms[i]
                    self.activeFormIndex = i
                }
            }
            .store(in: &cancellables)
        }
    }
}

Y, finalmente, la loginFormViewModel:

class LoginFormViewModel: ObservableObject {
    @Published var isActive: Bool
    
    let name: String
    let icon: Image
    let colorScheme: Color
    
    init(isActive: Bool = false, name: String, icon: Image, colorScheme: Color) {
        self.isActive = isActive
        self.name = name
        self.icon = icon
        self.colorScheme = colorScheme
    }
}

Básicamente, un botón en el formulario de inicio de sesión en sí establece su viewModel del isActive propiedad en true. Escuchar esto en TabBarViewModel y establecer el activeFormIndex en consecuencia. Este índice es utilizado en el bucle ForEach. Esencialmente, según el índice seleccionado, hay que generar más o menos espaciadores en el AnimatedTabSelector vista.

Sin embargo, mientras que el activeIndex variable se actualiza correctamente, el ForEach no parece reaccionar.

Actualización:

El AnimatedTabSelector se declara como parte de esta visión de conjunto:

struct TabIconsView: View {
    
    struct Constants {
        static let buttonDimensions: CGFloat = 50
        static let buttonIconSize: CGFloat = 25
        static let activeButtonColot = Color.white
        static let disabledButtonColor = Color.init(white: 0.8)
        
        struct Animation {
            static let stiffness: CGFloat = 330
            static let damping: CGFloat = 22
            static let velocity: CGFloat = 7
        }
    }
    
    @ObservedObject var tabBarViewModel: TabBarViewModel
    
    var body: some View {
        ZStack {
            AnimatedTabSelector(
                buttonDimensions: Constants.buttonDimensions,
                tabBarViewModel: tabBarViewModel)
            
            HStack {
                Spacer()
                
                ForEach(tabBarViewModel.loginForms) { loginForm in
                    Button(action: {
                        loginForm.loginFormViewModel.isActive = true
                    }) {
                        loginForm.loginFormViewModel.icon
                            .font(.system(size: Constants.buttonIconSize))
                            .foregroundColor(
                                tabBarViewModel.activeForm.id == loginForm.id ? Constants.activeButtonColot : Constants.disabledButtonColor
                            )
                    }
                    .frame(width: Constants.buttonDimensions, height: Constants.buttonDimensions)
                    Spacer()
                }
            }
        }
        .animation(Animation.interpolatingSpring(
            stiffness: Constants.Animation.stiffness,
            damping: Constants.Animation.damping,
            initialVelocity: Constants.Animation.velocity)
        )
    }
}

ACTUALIZACIÓN:

Traté de otra forma por la adición de otro publicado en la AnimatedTabSelector sí mismo para comprobar que los valores son, de hecho, que se actualiza en consecuencia. Así que al final de la HStack en este punto de vista he añadido:

.onAppear {
            tabBarViewModel.$activeFormIndex.sink { index in
                self.preCircleSpacers = index + 1
                self.postCircleSpacers = tabBarViewModel.loginForms.count - index
            }
            .store(in: &cancellables)
        }

Y, por supuesto, he añadido las siguientes variables para este punto de vista:

@State var preCircleSpacers = 1
@State var postCircleSpacers = 6
@State var cancellables = Set<AnyCancellable>()

A continuación, en los bucles ForEach he cambiado a:

ForEach(1..<preCircleSpacers)

y

ForEach(1..<postCircleSpacers)

respectivamente.

He añadido un punto de quiebre en el nuevo editor de la declaración y de hecho está siendo actualizado con la espera de las cifras. Pero la vista es que todavía no reflejan el cambio en los valores de

combine swift swiftui
2021-11-23 22:03:33
1

Mejor respuesta

0

OK, así que me parece que han encontrado una solución - lo estoy asumiendo es que ForEach que contiene un rango no se actualiza dinámicamente en la misma forma que el ForEach que contiene un array de objetos que hace.

Así que en lugar de:

ForEach(0..<tabBarViewModel.loginForms.count)

etc.

He cambiado a:

ForEach(tabBarViewModel.loginForms.prefix(tabBarViewModel.activeFormIndex))

De esta manera sigo repetir el número de veces necesario, pero el ForEach se actualiza de forma dinámica con el número correcto de elementos de la matriz

2021-11-23 23:51:48

Se parece a la documentación que se dice mucho: "La instancia sólo lee el valor inicial de los datos facilitados y no necesita identificar las vistas a través de las actualizaciones. Para calcular los puntos de vista sobre la demanda a través de una gama dinámica, el uso de ForEach/init(_:identificación:contenido:)."
Charles A.

En otros idiomas

Esta página está en otros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Slovenský
..................................................................................................................