SwiftUI - utilizzando la variabile di conteggio interno del ForEach

0

Domanda

Ho una vista creata attraverso un ciclo ForEach che ha bisogno di prendere una variabile di conteggio all'interno del ForEach in sè, cioè ho bisogno di una app per reagire a una dinamica di conte e di modificare la UI accoridngly.

Ecco la vista sto cercando di modificare:

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 il viewModel sto osservando:

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, infine, il 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
    }
}

In sostanza, un pulsante sul form di login imposta il viewModel è isActive proprietà su true. Ascoltiamo questo in TabBarViewModel e impostare il activeFormIndex di conseguenza. Questo indice viene poi utilizzato nel ciclo ForEach. In sostanza, secondo l'indice selezionato, ho bisogno di generare più o meno distanziali in AnimatedTabSelector vista.

Tuttavia, mentre il activeIndex variabile viene aggiornato correttamente, il ForEach non sembra reagire.

Aggiornamento:

Il AnimatedTabSelector dichiarato come parte di questa visione complessiva:

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

AGGIORNAMENTO:

Ho provato un altro modo aggiungendo un altro pubblicato per la AnimatedTabSelector a verificare che i valori non sono, infatti, essere aggiornato di conseguenza. Così, alla fine del HStack in questo punto di vista ho aggiunto:

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

E, naturalmente, ho aggiunto le seguenti variabili da questo punto di vista:

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

Quindi ForEach loop ho cambiato:

ForEach(1..<preCircleSpacers)

e

ForEach(1..<postCircleSpacers)

rispettivamente.

Ho aggiunto un punto di interruzione nel nuovo editore dichiarazione ed è infatti in fase di aggiornamento con le attese di figure. Ma la vista è ancora riusciti a riflettere le modifiche nei valori

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

Migliore risposta

0

OK quindi mi sembra di aver trovato una soluzione - che cosa sto presumendo che ForEach contenente un intervallo non aggiorna dinamicamente nello stesso modo in cui ForEach contenente un array di oggetti.

Quindi, piuttosto che:

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

ecc.

Ho cambiato:

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

In questo modo ho ancora iterare il numero necessario di volte, ma il ForEach si aggiorna in modo dinamico con il corretto numero di elementi nell'array

2021-11-23 23:51:48

Sembra la documentazione non dicono tanto: "L'istanza legge solo il valore iniziale dei dati forniti, e non ha bisogno di identificare vista attraverso gli aggiornamenti. Per calcolare vista sulla domanda in gamma dinamica, usare ForEach/init(_:id:contenuto:)."
Charles A.

In altre lingue

Questa pagina è in altre lingue

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