SwiftUI - à l'aide de la variable count dans ForEach

0

La question

J'ai une vue créée par une boucle ForEach qui doit prendre une variable de comptage dans le ForEach lui-même c'est à dire j'ai besoin de l'application pour réagir à une dynamique de comptage et de modifier l'INTERFACE utilisateur accoridngly.

Voici la vue que je suis en train de la modifier:

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)
        }
    }
}

Et le dernier que j'observe:

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)
        }
    }
}

Et enfin le 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
    }
}

En fait, un bouton sur le formulaire de connexion en elle-même définit son viewModel de l'est actif true à la propriété. Nous écoutent ce dans TabBarViewModel et définir la activeFormIndex en conséquence. Cet indice est ensuite utilisé dans la boucle ForEach. Essentiellement, en fonction de l'indice choisi, j'ai besoin de générer plus ou moins les entretoises dans le AnimatedTabSelector vue.

Cependant, alors que la activeIndex variable est correctement mis à jour, le ForEach ne semble pas réagir.

Mise à jour:

Le AnimatedTabSelector est déclaré dans le cadre de cette vue d'ensemble:

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)
        )
    }
}

Mise à JOUR:

J'ai essayé une autre manière, en ajoutant un autre publié le AnimatedTabSelector elle-même de vérifier que les valeurs sont en effet d'être mis à jour en conséquence. Ainsi, à la fin de la HStack dans cette vue, j'ai ajouté:

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

Et bien sûr, j'ai ajouté les variables suivantes de ce point de vue:

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

Puis, dans les boucles ForEach j'ai changé pour:

ForEach(1..<preCircleSpacers)

et

ForEach(1..<postCircleSpacers)

respectivement.

J'ai ajouté un point d'arrêt dans l'éditeur de déclaration et de il est en effet d'être mis à jour avec les chiffres. Mais la vue est toujours pas refléter les changements dans les valeurs

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

La meilleure réponse

0

OK, donc il me semble avoir trouvé une solution - ce que je suis en supposant que ForEach contenant une série à ne pas mettre à jour de façon dynamique dans la même manière que ForEach contenant un tableau d'objets n'.

Donc, plutôt que de:

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

etc.

Je l'ai changé pour:

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

De cette façon, j'ai toujours itérer le nombre de fois requis, mais le ForEach mises à jour de façon dynamique avec le bon nombre d'éléments dans le tableau

2021-11-23 23:51:48

Ressemble à de la documentation ne dit point: "L'instance ne lit que la valeur initiale des données fournies et n'a pas besoin d'identifier les points de vue à travers des mises à jour. Pour calculer les points de vue sur la demande sur une plage dynamique, l'utilisation de ForEach/init(_:id:contenu:)."
Charles A.

Dans d'autres langues

Cette page est dans d'autres langues

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