SwiftUI单击列表中的一行,将该列表中的每一行设置为true

Posted

技术标签:

【中文标题】SwiftUI单击列表中的一行,将该列表中的每一行设置为true【英文标题】:SwiftUI Clicking on one Row in a List, sets every row on that list to true 【发布时间】:2020-08-05 20:05:16 【问题描述】:

我有一个看起来像这样的列表:

但是当我点击一行时,每一行中的每个圆圈都会被激活/停用。我现在在这个问题上坐了几个小时,不知道该怎么办。谁能帮帮我?

我认为问题与DispatchQueue.main.async 线有关。但是如果我在代码中不使用DispatchQueue.main.async,则列表为空,无法填充。如果我不这样做DispatchQueue.main.async,我会两次收到以下错误:

Modifying state during view update, this will cause undefined behavior.

所以如果我使用DispatchQueue.main.async,列表可以被填充并且不为空。但正如我之前所描述的,新问题是,当我单击一行时,列表中的每个 Circle(来自所有行)都会被激活/停用。

这是我的代码:

struct zettelDetailView: View 
    
    var info: CreateInfo
    
    @State var isChecked: Bool = false
    
    func toggle()
        isChecked = !isChecked
    
    
    @State var listItems = [CreateZettelDetailViewText]()
    @State var executeThisAsyncThing: Bool = true

    
        private func changeInfoListToArray() 
            
            //String ist folgendermaßen aufgebaut:
            //"Text/-t;" t = true; f = false "/-" und ";" sind splitter
    //        var returnArray = [CreateZettelDetailViewText]()
            
    //        let infoStr = info.list
            let infoStr = "Das-t;Ist-f;Ein-f;Test-t;"
            
            let infoArr1 = infoStr.split(separator: ";")
            
            var infoArr2 = [[String]]()
            
            print(infoArr1.count)
            
            if infoArr1.count != 0 
                for i in 0...(infoArr1.count - 1) 
                    let x = infoArr1[i]
                    
                    let y = x.split(separator: "-")[0] //text
                    let z = x.split(separator: "-")[1] //bool
                    
                    var arr2 = [String]()
                    arr2.append(String(y))
                    arr2.append(String(z))

                    var zBool: Bool = false
                    if z == "t" 
                        zBool = true
                     else if z == "f" 
                        zBool = false
                    
                    
                    infoArr2.append(arr2)
                    
                    self.listItems.append(CreateZettelDetailViewText(text: "\(String(y))", isDone: zBool))
                    
                
                            
            
            
            executeThisAsyncThing = false

        
    
    

    
    var body: some View 
        
//        listItems = changeInfoListToArray()
        

      DispatchQueue.main.async 
          if self.executeThisAsyncThing 
              self.changeInfoListToArray()
          
      
            
        
        return VStack 
            
            VStack 
                Text(info.name)
                   .font(.largeTitle)
                Text("\(info.day) - \(info.time)")
            .offset(y: -50)
            
            
            List(listItems)  a in

                Button(action: self.toggle
//                    
//
//                    if a.isDone 
//                        self.isChecked = true
//                     else if !a.isDone 
//                        self.isChecked = false
//                    
//
//                    print()
//                    print(a.id)
//                    print(a.text)
//                    print(a.isDone)
//                    print()
//
//                
                )
                    HStack 
                        Image(systemName: self.isChecked ? "checkmark.circle.fill" : "circle")
                        Text(a.text)
                    
                .onAppear() 
                    self.isChecked = true
                

            .offset(y: -50)


            Button(action: addRow) 
                Image(systemName: "plus.app.fill")
                    .offset(y: -20)
                    .font(.largeTitle)
            




        
        
    
    
        private func addRow() 
                    
//            var arr = [[String]]()
//            arr = getZettelData()
//            arr.append(["ZettelName", "\(time)", "\(day)"])
//
//            let defaults = UserDefaults.standard
//            defaults.set(arr, forKey: "SavedZettelArray")

        
    
    

【问题讨论】:

【参考方案1】:

整个列表只有一个isChecked。每一行都使用相同的变量,因此当其中一个被点击时,它们都会改变。

根据代码,列表中的每个项目都有一个isDone 成员。尝试将 self.isChecked 替换为 a.isDone 作为图像名称。然后尝试将按钮操作从self.toggle 更改为 a.isDone.toggle()

所以你的 List 如下所示。

List(listItems)  a in
    Button(action:  
        a.isDone.toggle()
    )
        HStack 
            Image(systemName: a.isDone ? "checkmark.circle.fill" : "circle")
            Text(a.text)
        
    
.offset(y: -50)

【讨论】:

这可以作为评论发布。在您的答案中添加解决 OP 问题的方法。 我同意。但是,我没有足够的代表发表评论。我会在回复中添加更多内容以使其成为解决方案。 在 SwiftUI 测试版中有一个名为Label 的新组件,它具有图像和文本。基本上可以用来代替HStack。确实取决于所使用的 SwiftUI 版本 :) 我尝试了你的解决方案,但它对我不起作用,因为Cannot assign to property: 'a' is a 'let' constant 你知道如何切换我的a 吗?

以上是关于SwiftUI单击列表中的一行,将该列表中的每一行设置为true的主要内容,如果未能解决你的问题,请参考以下文章

Haskell:将文件中的每一行插入到列表中

Power Query:如何为列表中的每一行添加列

如何在clojure中的每一行打印数字列表?

如何将 csv 文件读入 SWI prolog 中的列表列表,其中内部列表代表 CSV 的每一行?

在列表行中制作等宽的 SwiftUI 视图

为 javaFx 表视图中的每一行动态填充组合框列表