Foreach'in içinde değişken bir sayım yapması gereken bir ForEach döngüsü aracılığıyla oluşturulmuş bir görünümüm var, yani uygulamanın dinamik bir sayıma tepki vermesi ve kullanıcı arayüzünü uyumlu bir şekilde değiştirmesi gerekiyor.
İşte değiştirmeye çalıştığım görünüm:
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)
}
}
}
Ve gözlemlediğim viewModel:
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)
}
}
}
Ve son olarak 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
}
}
Temel olarak, oturum açma formundaki bir düğme, Viewmodel'in isActive özelliğini true olarak ayarlar. Bunu Tabbarviewmodel'de dinliyoruz ve activeformındex'i buna göre ayarlıyoruz. Bu dizin daha sonra ForEach döngüsünde kullanılır. Esasen, seçilen dizine bağlı olarak, AnimatedTabSelector görünümünde daha fazla veya daha az aralayıcı oluşturmam gerekiyor.
Ancak, activeIndex değişkeni doğru güncelleştirilirken, ForEach yanıt vermiyor gibi görünüyor.
Güncelleme:
AnimatedTabSelector bu genel görünümün bir parçası olarak bildirilir:
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)
)
}
}
Güncelleme:
Değerlerin gerçekten buna göre güncellendiğini kontrol etmek için animatedtabselector'a yayınlanmış başka bir yol ekleyerek başka bir yol denedim. Bu yüzden bu görünümdeki Hstack'in sonunda ekledim:
.onAppear {
tabBarViewModel.$activeFormIndex.sink { index in
self.preCircleSpacers = index + 1
self.postCircleSpacers = tabBarViewModel.loginForms.count - index
}
.store(in: &cancellables)
}
Ve elbette bu görünüme aşağıdaki değişkenleri ekledim:
@State var preCircleSpacers = 1
@State var postCircleSpacers = 6
@State var cancellables = Set<AnyCancellable>()
Sonra ForEach döngülerinde değiştirdim:
ForEach(1..<preCircleSpacers)
ve
ForEach(1..<postCircleSpacers)
sırasıyla.
Yeni yayıncı bildirimine bir kesme noktası ekledim ve gerçekten beklenen rakamlarla güncelleniyor. Ancak görünüm hala değerlerdeki değişimi yansıtmıyor