【发布时间】:2022-01-02 09:20:53
【问题描述】:
我有一个通过 ForEach 循环创建的视图,它需要在 ForEach 本身内获取变量计数,即我需要应用对动态计数做出反应并相应地更改 UI。
这是我要修改的视图:
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)
}
}
}
还有我正在观察的 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)
}
}
}
最后是 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
}
}
基本上,登录表单上的按钮本身会将其 viewModel 的 isActive 属性设置为 true。我们在 TabBarViewModel 中监听这个并相应地设置 activeFormIndex。然后在 ForEach 循环中使用该索引。本质上,根据选择的索引,我需要在 AnimatedTabSelector 视图中生成或多或少的分隔符。
然而,虽然 activeIndex 变量被正确更新,但 ForEach 似乎没有反应。
更新:
AnimatedTabSelector 被声明为这个整体视图的一部分:
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)
)
}
}
更新:
我尝试了另一种方法,将另一个发布到 AnimatedTabSelector 本身以检查值是否确实在相应地更新。所以在这个视图的 HStack 末尾我添加了:
.onAppear {
tabBarViewModel.$activeFormIndex.sink { index in
self.preCircleSpacers = index + 1
self.postCircleSpacers = tabBarViewModel.loginForms.count - index
}
.store(in: &cancellables)
}
当然,我在这个视图中添加了以下变量:
@State var preCircleSpacers = 1
@State var postCircleSpacers = 6
@State var cancellables = Set<AnyCancellable>()
然后在 ForEach 循环中我改为:
ForEach(1..<preCircleSpacers)
和
ForEach(1..<postCircleSpacers)
分别。
我在新的发布者声明中添加了一个断点,它确实正在更新为预期的数字。但观点仍然未能反映价值观的变化
【问题讨论】:
-
您是否在您的 SwiftUI 代码中设置了断点来验证
tabBarViewModel.loginForms.count和tabBarViewModel.activeFormIndex是您认为的值? -
是的,我有 - 他们肯定会返回预期值
-
所以这些值是正确的,但是 ForEach 没有迭代与这些值相关的正确次数,或者 SwiftUI 主体根本不会再次被调用?我不确定您所说的“似乎没有反应”是什么意思。
-
抱歉 - 是的,所以不会再次调用 SwiftUI 主体。在第一次加载时一切都很好,但是当这些值发生变化时,视图不会改变,即垫片的数量不会随着值的变化而变化
-
看起来您没有遗漏任何明显的 SwiftUI 注释。假设您已验证传递给 sink 的闭包正在按预期调用,我的猜测是您的
TabBarViewModel实例比您预期的要多,并且正在观察一个实例,而另一个正在更新。