SwiftUI - utilizando a variável de contagem dentro do ForEach

0

Pergunta

Eu tenho um modo de exibição criado através de um loop ForEach, que precisa tomar uma variável de contagem dentro do ForEach si i.e. eu preciso do app para reagir a uma dinâmica de contagem e mudar a INTERFACE do usuário accoridngly.

Aqui é o ponto de vista que eu estou tentando 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)
        }
    }
}

E o viewModel estou observando:

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

E, finalmente, o 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
    }
}

Basicamente, um botão no formulário de login o próprio define seu viewModel do isActive propriedade para true. Ouvimos isso em TabBarViewModel e definir o activeFormIndex de acordo. Este índice é usado no loop ForEach. Essencialmente, dependendo do índice selecionado, preciso gerar mais ou menos espaçadores no AnimatedTabSelector vista.

No entanto, enquanto o activeIndex variável está sendo atualizado corretamente, o ForEach não parece reagir.

Atualização:

O AnimatedTabSelector é declarada como parte dessa visão geral:

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

ATUALIZAÇÃO:

Eu tentei de outra forma pela adição de um outro publicado para o AnimatedTabSelector próprio para verificar que os valores são, de fato, sendo actualizado em conformidade. Assim, no final do HStack neste modo de exibição disse:

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

E, claro, eu adicionei as seguintes variáveis para este modo de exibição:

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

Em seguida, no loops ForEach eu alterada para:

ForEach(1..<preCircleSpacers)

e

ForEach(1..<postCircleSpacers)

respectivamente.

Eu adicionei um ponto de interrupção no novo editor de declaração e é, de fato, sendo atualizado com a expectativa de figuras. Mas a visão ainda está falhando para refletir a alteração nos valores

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

Melhor resposta

0

OK, então me parece ter encontrado uma solução - o que eu estou presumindo que é que o ForEach, que contém um intervalo não atualiza dinamicamente, da mesma forma que o ForEach, que contém uma matriz de objetos faz.

Assim, em vez de:

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

etc.

Mudei para:

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

Desta forma, eu ainda iterar o número necessário de vezes, mas o ForEach é atualizada dinamicamente com o número correto de itens na matriz

2021-11-23 23:51:48

Parece que a documentação não dizem muito: "A instância só lê o valor inicial dos dados fornecidos e não precisa identificar pontos de vista através de atualizações. Para calcular pontos de vista sobre a demanda através de uma gama dinâmica, usar ForEach/init(_:id:conteúdo:)."
Charles A.

Em outros idiomas

Esta página está em outros idiomas

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