SwiftUI - 如何切换核心数据中的所有布尔值

Posted

技术标签:

【中文标题】SwiftUI - 如何切换核心数据中的所有布尔值【英文标题】:SwiftUI - How to toogle all the booleans in a CoreData 【发布时间】:2020-04-23 21:19:02 【问题描述】:

我有一个数据库,其中包含多个以布尔值作为属性的对象。我正在寻找一个函数来在我按下按钮时反转所有布尔对象。我尝试了这个功能,但显示了几个错误,例如('Bool' 类型的值没有成员'indices'):

struct ViewList: View 
  @Environment(\.managedObjectContext) var context
  @State var newName: String = ""
  @FetchRequest(
      entity: Product.entity(),
      sortDescriptors: [NSSortDescriptor(keyPath: \Product.name, ascending: true)]
  ) var list: FetchedResults<Product>

  var body: some View 
      VStack 
          HStack 
                TextField("I insert the name of the product", text: $newName)
              Button(action:  self.add()
                               self.newName = ""
              )
               Image(systemName: "plus") 
          
          List 
              ForEach(list, id: \.self) 
                  product in ViewItem(product: product)
              
          
      
  
  public func add() 
      let newProduct = Product(context: context)
      newProduct.name = newName
      do 
          try context.save()
       catch 
          print(error)
      
  
                       


struct ViewItem: View 
  @State var product: Product
  @State var refresh: Bool = false

  var body: some View 
      NavigationLink(destination: ViewDetail(product: product, refresh: $refresh)) 
          HStack(alignment: .top) 
              Button( action: 
                  self.clean()
                  self.product.isSelected.toggle()
              ) 
                  if self.product.isSelected == true 
                      Image(systemName: "checkmark")
                   else 
                      Image(systemName: "checkmark").colorInvert()
                  
              
              VStack() 
                  Text(product.name)
                  if product.password != "" 
                      Text("Password : " + product.password)
                  
                  Text(String(refresh)).hidden()
              
          
      
      .onAppear 
          self.refresh = false
      
  

我一直在想,但我不知道该怎么做……

func clean() 
    for( index ) in self.product.isSelected.indices 
        self.product[index]isSelected = false
    

【问题讨论】:

您的 var 产品是 Product 的单个实例。看起来作为 FetchRequest 输出的 var 列表是您应该迭代的东西。 @WarrenBurton : 我想把函数放在结构 ViewItem 中,当我在结构 VeiwItem 中时如何检索数据库“列表”? 您想翻转列表中所有产品的选中状态?还是只是 ViewItem 中的一种产品? 我明白了,你有一个表格视图,当你选择一个项目时,你想取消选择之前的选择。在数据(模型)中存储选择(视图)状态并不是一个很好的设计选择。一种可能的模式是创建一个选择处理程序类来记住选择状态,将该对象传递给 ViewItem 并使用它来更改选择。看看raywenderlich.com/7705231-creating-a-mind-map-ui-in-swiftui中的选择机制,我明天会尝试回答问题 @WarrenBurton:谢谢你的链接,我会研究这个。我已将布尔值 isSelected 放入实体产品中,因为我想将其存储在 Coredata 中,并在下次启动应用程序时重新查找所选产品。因此,当显示产品的 ViewDetail 时,必须在数据库中取消选择之前选择的产品。 【参考方案1】:

您需要创建一个查询来翻转isSelected 标志的状态。最好不要将此逻辑置于视图系统之外,以便您可以在任何地方使用它。

你创建一个SelectionHandler

import CoreData

class SelectionHandler 

    func clearSelection(in context: NSManagedObjectContext) 
        for item in currentSelected(in: context) 
            item.isSelected = false
        
    

    func selectProduct(_ product: Product) 
        guard let context = product.managedObjectContext else 
            assertionFailure("broken !")
            return
        

        clearSelection(in: context)
        product.isSelected = true
    

    func currentSelected(in context: NSManagedObjectContext) -> [Product] 
        let request = NSFetchRequest<Product>(entityName: Product.entity().name!)
        let predicate = NSPredicate(format: "isSelected == YES")
        request.predicate = predicate

        do 
            let result = try context.fetch(request)
            return result
         catch  
            print("fetch error =",error)
            return []
        

    


然后您可以使用它来选择您想要的产品。

SelectionHandler().selectProduct(product)

就目前而言,您的 NavigationLink 不会执行任何操作,因为父列表未保存在 NavigationView 中,因此您需要将 ViewList 的正文更改为如下所示。

var body: some View 
    NavigationView 
        VStack 
            HStack 
                TextField("Create product with name", text: $newName)
                Button(action: 
                    self.add()
                    self.newName = ""
                )
                 Image(systemName: "plus") 
            
            .padding()
            List 
                ForEach(list, id: \.self)  product in
                    ViewItem(product: product)
                
            
        
    

ViewItem 中,Product 应该是 ObservedObject,以便在 managedObject 中检测到更改。

struct ViewItem: View 

    @ObservedObject var product: Product
    @State var refresh: Bool = false

    var checkmarkImage: some View 
        return Group 
            if self.product.isSelected 
                Image(systemName: "checkmark")
             else 
                Image(systemName: "checkmark").colorInvert()
            
        
    

    var body: some View 
        NavigationLink(destination: ViewDetail(product: product, refresh: $refresh)) 
            HStack 
                checkmarkImage
                Text(product.name ?? "wat")
            
        
    

原来的Button 不会与NavigationLink 一起使用,但您可以简单地将选择应用于ViewDetail 中的onAppear

struct ViewDetail: View 

    @ObservedObject var product: Product
    @Binding var refresh: Bool

    var body: some View 
        VStack 
            Text("Hello, World!")
            Text("Product is \(product.name ?? "wat")")
        
        .onAppear 
            SelectionHandler().selectProduct(self.product)
        
    

【讨论】:

非常感谢!正是我需要的。再次感谢。

以上是关于SwiftUI - 如何切换核心数据中的所有布尔值的主要内容,如果未能解决你的问题,请参考以下文章

UITableView Swift中带有布尔值的切换按钮

更改布尔值时无法在 SwiftUI 中更改按钮文本

如何选择一个 TableViewCell 并将所有其他单元格设置为 CoreData 中的布尔值 NO?

SwiftUI:如何从核心数据中的 TextField 中保存值

删除具有绑定布尔值的列表项 - SwiftUI

如何在 SwiftUI 中使用 CoreData 更改数据值?