英文:
ForEach runs only for specific times on update
问题
以下是您要翻译的代码部分:
I have a view defined below. On buttons click , `Go Back` and `save & continue` , I update the `currentIndex`.
My LeftView ForEach exhibiting a strange behavior. It calls the for loop only limited number of times. Ideally it should call the `let _ = print(index)` 5 times. However it calls it 5 times only on initial render and afterwards, if I change the value of `currentIndex`, the loop is called twice for different indexes each time. The log for
let _ = print("\(title) \(self.isActive)") inside BulletView is
Initially ->
Customer Information true
System Information false
Payment false
Add Template false
Receipt false
One 1x increment when currentIndex is 1
Customer Information true
System Information false
One 2nd increment when currentIndex is 2
System Information false
Payment false
Why is it exhibiting this behavior and why other `BulletViews` are not updated.
fileprivate let titlesList = ["Customer Information",
"System Information",
"Payment",
"Add Template",
"Receipt"]
struct InstallationView: View {
@ObservedObject private var viewModel = InstallationViewModel()
var body: some View {
NavigationView {
GeometryReader { metrics in
HStack(alignment: .center, spacing: 0) {
LeftView(currentIndex: $viewModel.currentIndex)
.frame(width: metrics.size.width * 0.35)
.frame(maxHeight: .infinity)
RightView(currentIndex: $viewModel.currentIndex)
.frame(width: metrics.size.width * 0.65)
.frame(maxHeight: .infinity)
}
}
}.navigationBarHidden(true).navigationViewStyle(StackNavigationViewStyle())
}
}
fileprivate struct LeftView: View {
@Binding var currentIndex: Int
var body: some View {
VStack(alignment: .center, spacing: 0) {
ForEach(0..<titlesList.count, id: \.self) { index in
let _ = print(index)
BulletView(count: index + 1,
title: titlesList[index],
isActive: (currentIndex == index),
showTopSeparator: (index != 0),
showBottomSeparator: (index != titlesList.count - 1))
}
}.padding(EdgeInsets.init(top: 0, leading: 100, bottom: 0, trailing: 0))
}
}
fileprivate struct RightView: View {
@Binding var currentIndex: Int
private let edges = EdgeInsets.init(top: 0, leading: 0, bottom: 20, trailing: 20)
private let leftEdges = EdgeInsets.init(top: 0, leading: 10, bottom: 0, trailing: 0)
@Environment(\.presentationMode) var presentationMode
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
Text(titlesList[currentIndex])
.font(Font(UIFont.boldSystemFont(ofSize: 24)))
.foregroundColor(Color.black)
.padding(EdgeInsets(top: 20, leading: 0, bottom: 0, trailing: 0))
containedView()
HStack {
Button(action: { currentIndex -= 1 }) {
Text("Go Back")
.frame(width: 200, height: 70)
.font(Font(UIFont.boldSystemFont(ofSize: 24)))
.foregroundColor(.white)
.background(Color(Constants.Colors.main))
.cornerRadius(5)
}
Spacer()
Button(action: { currentIndex += 1 }) {
Text("Save & Continue")
.frame(width: 200, height: 70)
.font(Font(UIFont.boldSystemFont(ofSize: 24)))
.foregroundColor(.white)
.background(Color(Constants.Colors.main))
.cornerRadius(5)
}
}
.padding(edges)
}.padding(EdgeInsets.init(top: 80, leading: 0, bottom: 0, trailing: 0))
}
}
func containedView() -> AnyView {
switch currentIndex {
case 0:
return AnyView(CustomerView())
case 1:
return AnyView(SystemView())
case 2:
return AnyView(PaymentView())
default:
return AnyView(CustomerView())
}
}
}
struct InstallationView_Previews: PreviewProvider {
static var previews: some View {
InstallationView().previewInterfaceOrientation(.landscapeLeft)
}
}
希望这对您有所帮助!
英文:
I have a view defined below. On buttons click , Go Back
and save & continue
, I update the currentIndex
.
My LeftView ForEach exhibiting a strange behavior. It calls the for loop only limited number of times. Ideally it should call the let _ = print(index)
5 times. However it calls it 5 times only on initial render and afterwards, if I change the value of currentIndex
, the loop is called twice for different indexes each time. The log for
let _ = print("\(title) \(self.isActive)") inside BulletView is
Initially ->
Customer Information true
System Information false
Payment false
Add Template false
Receipt false
One 1x increment when currentIndex is 1
Customer Information true
System Information false
One 2nd increment when currentIndex is 2
System Information false
Payment false
Why is it exhibiting this behavior and why other BulletViews
are not updated.
fileprivate let titlesList = ["Customer Information",
"System Information",
"Payment",
"Add Template",
"Receipt"]
struct InstallationView: View {
@ObservedObject private var viewModel = InstallationViewModel()
var body: some View {
NavigationView {
GeometryReader { metrics in
HStack(alignment: .center, spacing: 0) {
LeftView(currentIndex: $viewModel.currentIndex)
.frame(width: metrics.size.width * 0.35)
.frame(maxHeight: .infinity)
RightView(currentIndex: $viewModel.currentIndex)
.frame(width: metrics.size.width * 0.65)
.frame(maxHeight: .infinity)
}
}
}.navigationBarHidden(true).navigationViewStyle(StackNavigationViewStyle())
}
}
fileprivate struct LeftView: View {
@Binding var currentIndex: Int
var body: some View {
VStack(alignment: .center, spacing: 0) {
ForEach(0..<titlesList.count, id: \.self) { index in
let _ = print(index)
BulletView(count: index + 1,
title: titlesList[index],
isActive: (currentIndex == index),
showTopSeparator: (index != 0),
showBottomSeparator: (index != titlesList.count - 1))
}
}.padding(EdgeInsets.init(top: 0, leading: 100, bottom: 0, trailing: 0))
}
}
fileprivate struct RightView: View {
@Binding var currentIndex: Int
private let edges = EdgeInsets.init(top: 0, leading: 0, bottom: 20, trailing: 20)
private let leftEdges = EdgeInsets.init(top: 0, leading: 10, bottom: 0, trailing: 0)
@Environment(\.presentationMode) var presentationMode
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
Text(titlesList[currentIndex])
.font(Font(UIFont.boldSystemFont(ofSize: 24)))
.foregroundColor(Color.black)
.padding(EdgeInsets(top: 20, leading: 0, bottom: 0, trailing: 0))
containedView()
HStack {
Button(action: { currentIndex -= 1 }) {
Text("Go Back")
.frame(width: 200, height: 70)
.font(Font(UIFont.boldSystemFont(ofSize: 24)))
.foregroundColor(.white)
.background(Color(Constants.Colors.main))
.cornerRadius(5)
}
Spacer()
Button(action: { currentIndex += 1 }) {
Text("Save & Continue")
.frame(width: 200, height: 70)
.font(Font(UIFont.boldSystemFont(ofSize: 24)))
.foregroundColor(.white)
.background(Color(Constants.Colors.main))
.cornerRadius(5)
}
}
.padding(edges)
}.padding(EdgeInsets.init(top: 80, leading: 0, bottom: 0, trailing: 0))
}
}
func containedView() -> AnyView {
switch currentIndex {
case 0:
return AnyView(CustomerView())
case 1:
return AnyView(SystemView())
case 2:
return AnyView(PaymentView())
default:
return AnyView(CustomerView())
}
}
}
struct InstallationView_Previews: PreviewProvider {
static var previews: some View {
InstallationView().previewInterfaceOrientation(.landscapeLeft)
}
}
答案1
得分: 1
不要回答我要翻译的问题。
以下是要翻译的内容:
不要使用索引作为ForEach
的标识符。对于给定的实体,标识符必须是稳定的。
所以这一行是错误的:
ForEach(0..<titlesList.count, id: \.self) { index in
如果不更改元素的数量,那么SwiftUI将假定没有更改(id 0是第一个,id 1是第二个,依此类推)。
相反,应该是:
ForEach(titlesList) { title in
而且titleList
的元素应该是一个Identifiable
结构。
话虽如此,在SwiftUI视图中,let _ = print(index)
是没有意义的。SwiftUI视图不是可执行代码。它们描述视图,而不是创建视图。body
可以被评估多次(这就是为什么它是一个属性,而不是一个函数),或者它可以被缓存或复制。您不应该期望它的评估与刷新视图之间有任何关系。
英文:
It is not valid to use an index for ForEach
as the identifier. The identifier is required to be stable for a given entity.
So this line is wrong:
ForEach(0..<titlesList.count, id: \.self) { index in
If you do not change the number of elements, then SwiftUI will assume that there are no changes (id 0 is first, id 1 is second, etc).
Instead, this should be:
ForEach(titlesList) { title in
And the element of titleList
should be an Identifiable
struct.
That said, let _ = print(index)
is meaningless in a SwiftUI View. SwiftUI views are not executable code. They describe the View. They do not create the View. body
can be evaluated many times (that's why it's a property, not a function), or it may be cached or copied. You should not expect it being evaluated to have any relationship to the View being refreshed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论