英文:
View row does not reflect correct value
问题
以下是您要翻译的内容:
"I am trying to display a list of items, each item having an index and a visibility flag. When I change the visibility of the item, and then move it down the list, the visibility of the item is not correctly reflected in the list.
To reproduce the issue 1) add 3 items by tapping on the + button, 2) change visibility of PLOT 1 by tapping on the eye icon, 3) tap the down icon on PLOT 1 swapping it with PLOT 2. I am expecting that PLOT 1 will have visibility false and PLOT 2 will have visibility true, but instead they are reversed: PLOT 2 has visibility false, and PLOT 1 has visibility true, which is incorrect.
This code is running in macOS 13.0 app in Xcode 14.2 Swift 5"
以上内容已被翻译。
英文:
I am trying to display a list of items, each item having an index and a visibility flag. When I change the visibility of the item, and then move it down the list, the visibility of the item is not correctly reflected in the list.
To reproduce the issue 1) add 3 items by tapping on the + button, 2) change visibility of PLOT 1 by tapping on the eye icon, 3) tap the down icon on PLOT 1 swapping it with PLOT 2.  I am expecting that PLOT 1 will have visibility false and PLOT 2 will have visibility true, but instead they are reversed: PLOT 2 has visibility false, and PLOT 1 has visibility true, which is incorrect.
This code is running in macOS 13.0 app in Xcode 14.2 Swift 5
import SwiftUI
struct ContentView: View {
    
    class Plot : Equatable {
        static func == (lhs: ContentView.Plot, rhs: ContentView.Plot) -> Bool {
            lhs === rhs
        }
        
        internal init(index: Int, visible: Bool = true) {
            self.index = index
            self.visible = visible
        }
        
        let index : Int
        var visible : Bool = true
    }
    
    struct PlotView : View {
        
        let plot : Plot
        
        struct Delegate {
            let moveDown : ()->Void
            let canMoveDown : ()->Bool
        }
        
        let delegate : Delegate
        
        @State private var visible = false
        
        var body : some View {
            HStack {
                Text("PLOT \(plot.index)")
                
                Button(action: {
                    visible.toggle()
                }, label: {
                    Image(systemName: "eye.circle" + (visible ? ".fill" : ""))
                })
                
                Button(action: {
                    delegate.moveDown()
                }, label: {
                    Image(systemName: "chevron.down.circle")
                })
                .disabled(!delegate.canMoveDown())
            }
            .onAppear {
                visible = plot.visible
            }
            .onChange(of: visible) { v in
                plot.visible = v
                print("changed plot[\(plot.index)].visible = \(plot.visible ? "true" : "false")")
            }
        }
    }
    
    @State private var plots : [Plot] = []
    
    var body: some View {
        VStack {
            
            ForEach(0..<plots.count, id: \.self) { i in
                PlotView.init(plot: plots[i], delegate: .init(moveDown: {
                    if i < plots.count-1 {
                        plots.swapAt(i, i+1)
                    } else {
                        plots.insert(plots.removeLast(), at: 0)
                    }
                }, canMoveDown: {
                    !plots.isEmpty
                }))
            }
            Button(action: {
                plots.append(.init(index: plots.count))
            }, label: {
                Image(systemName: "plus.circle")
            })
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
答案1
得分: 1
I cannot say exactly why it doesn't work but it works with a Plot struct and @Binding
Don't nest the things, it's bad practice.
This is Plot, it became pretty tiny
struct Plot : Equatable, Identifiable {
    let id = UUID()
    let index : Int
    var visible : Bool = true
}
Edit: In ContentView the ForEach expression iterates the element rather than the indices
struct ContentView: View {
    @State private var plots : [Plot] = []
        
    var body: some View {
        VStack {
            ForEach($plots) { $plot  in
                PlotView.init(plot: $plot, delegate: .init(moveDown: {
                    if let i = plots.firstIndex(of: plot),
                       i < plots.count-1 {
                        plots.swapAt(i, i+1)
                    } else {
                        plots.insert(plots.removeLast(), at: 0)
                    }
                }, canMoveDown: {
                    !plots.isEmpty
                }))
            }
            
            Button(action: {
                plots.append(.init(index: plots.count))
            }, label: {
                Image(systemName: "plus.circle")
            })
        }
    }
}
In Plotview the Plot instance is bound and the extra visible property as well as the .onChange modifier are not needed.
struct PlotView : View {
    @Binding var plot : Plot
    
    struct Delegate {
        let moveDown : ()->Void
        let canMoveDown : ()->Bool
    }
    
    let delegate : Delegate
    
    var body : some View {
        HStack {
            Text("PLOT \(plot.index)")
            
            Button(action: {
                plot.visible.toggle()
            }, label: {
                Image(systemName: "eye.circle" + (plot.visible ? ".fill" : ""))
            })
            
            Button(action: {
                delegate.moveDown()
            }, label: {
                Image(systemName: "chevron.down.circle")
            })
            .disabled(!delegate.canMoveDown())
        }
    }
}
英文:
I cannot say exactly why it doesn't work but it works with a Plot struct and @Binding
Don't nest the things, it's bad practice.
This is Plot, it became pretty tiny
struct Plot : Equatable, Identifiable {
    let id = UUID()
    let index : Int
    var visible : Bool = true
}
Edit: In ContentView the ForEach expression iterates the element rather than the indices
struct ContentView: View {
    
    @State private var plots : [Plot] = []
        
        var body: some View {
            VStack {
                ForEach($plots) { $plot  in
                    PlotView.init(plot: $plot, delegate: .init(moveDown: {
                        if let i = plots.firstIndex(of: plot),
                           i < plots.count-1 {
                            plots.swapAt(i, i+1)
                        } else {
                            plots.insert(plots.removeLast(), at: 0)
                        }
                    }, canMoveDown: {
                        !plots.isEmpty
                    }))
                }
                
                Button(action: {
                    plots.append(.init(index: plots.count))
                }, label: {
                    Image(systemName: "plus.circle")
                })
            }
        }
    }
}
In Plotview the Plot instance is bound and the extra visible property as well as the .onChange modifier are not needed.
struct PlotView : View {
      
      @Binding var plot : Plot
      
      struct Delegate {
          let moveDown : ()->Void
          let canMoveDown : ()->Bool
      }
      
      let delegate : Delegate
      
      var body : some View {
          HStack {
              Text("PLOT \(plot.index)")
              
              Button(action: {
                  plot.visible.toggle()
              }, label: {
                  Image(systemName: "eye.circle" + (plot.visible ? ".fill" : ""))
              })
              
              Button(action: {
                  delegate.moveDown()
              }, label: {
                  Image(systemName: "chevron.down.circle")
              })
              .disabled(!delegate.canMoveDown())
          }
      }
  }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。





评论