ForEach仅在更新时运行特定次数。

huangapple go评论69阅读模式
英文:

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..&lt;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.

huangapple
  • 本文由 发表于 2023年4月11日 02:41:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979776.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定