【问题标题】:SwiftUI How to make CoreData list selectable and double clickable on macOSSwiftUI 如何使 CoreData 列表在 macOS 上可选择和可双击
【发布时间】:2020-12-09 18:11:54
【问题描述】:

我有一个可选择和双击的名称列表。 我有一个 CoreData 记录列表,我无法选择或双击。

如何使 CoreData 列表可选择和双击?

这是我正在使用的可选列表代码:

import SwiftUI

struct ContentView: View {
    private static let values = ["Luke","Han","Chewie","Liea","R2D2"]
    
    @State private var selection: String? = nil
    
    var body: some View {
        VStack {
            HStack {
                List(Self.values, id: \.self, selection: $selection) { name in
                    Text(name)
                        .gesture(TapGesture(count: 2).onEnded {
                            print("double clicked")
                            selection = name
                        })
                        .simultaneousGesture(TapGesture().onEnded {
                            print("single clicked")
                            selection = name
                        })
                }
            }
        }
    }
}

这是我的 ManagedObject:

@objc(Enquiry)
public class Enquiry: NSManagedObject {

}

这是显示列表的 CoreData 列表:

import SwiftUI
import CoreData

struct EnquiryList: View
{
    @Environment(\.managedObjectContext) private var viewContext
    
    var enquiries: FetchRequest<Enquiry>
    
    init (uuid: UUID)
    {
        enquiries = FetchRequest<Enquiry>(entity: Enquiry.entity(), sortDescriptors: [], predicate: NSPredicate(format: "UUID == %@", uuid as CVarArg))
    }
    @State private var selection: Enquiry? = nil
    
    var body: some View
    {
        VStack
        {
            ForEach(enquiries, id: \.self, selection: $selection)
            {enquiry in
                EnquiryListRow(enquiry: enquiry)
                    .gesture(TapGesture(count: 2).onEnded {
                        print("double clicked")
                        selection = enquiry
                    })
            }
        }
    }
}

ForEach 行给出以下错误:

我错过了什么?最终我想选择记录,然后双击打开一个带有查询的窗口。

根据 Asperi 的回答更改代码并没有让我更进一步:

List(enquiries.wrappedValue, id: \.self, selection: $selection)
{enquiry in
    Text("Hello")
}

enquiries.wrappedValue.count 显示有 3 条记录,但没有任何显示。将其移回 ForEach 将显示 Hello 三次。

【问题讨论】:

  • 第二个例子不起作用?第一个对我来说很好
  • 是的,这是我的问题。如何使第二个示例像第一个示例一样工作。

标签: macos core-data swiftui


【解决方案1】:

如果没有您丢失的代码,重现错误有点困难。不过,我已经编写了一些代码,您可以复制和粘贴这些代码以使工作正常进行。

请在下面找到我经过测试和工作的示例:

import SwiftUI
import Foundation
import CoreData

class PersistentContainer {
    public static var container: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "DataModel")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                    fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        
        return container
    }()
    
    private init() {}
}

class DataModel: ObservableObject {
    private var _context: NSManagedObjectContext
    @Published public var Enquiries: [Enquiry]
    
    init() {
        self._context = PersistentContainer.container.viewContext
        self.Enquiries = try! self._context.fetch(Enquiry.fetchRequest())
    }
}

struct EnquiryListRow: View {
    public var Enquiry: Enquiry
    
    var body: some View {
        Text("\(self.Enquiry.name ?? "(no name given)")")
    }
}

struct ContentView: View {
    @StateObject var model = DataModel()
    @State private var selection: Enquiry? = nil
    
    var body: some View {
        List(self.model.Enquiries, id: \.self, selection: self.$selection) { enquiry in
            EnquiryListRow(Enquiry: enquiry)
                .gesture(TapGesture(count: 2).onEnded {
                    print("double clicked")
                    self.selection = enquiry
                })
                .simultaneousGesture(TapGesture().onEnded {
                    print("single clicked")
                    self.selection = enquiry
                })
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

【讨论】:

    【解决方案2】:

    您也应该在第二种情况下使用 List - ForEach 只是一个没有 selection 功能的动态组:

    var body: some View
    {
        VStack
        {
            List(selection: $selection)     // << here !!
            {ForEach(enquiries, id: \.self)
            {enquiry in
                EnquiryListRow(enquiry: enquiry)
                    .gesture(TapGesture(count: 2).onEnded {
                        print("double clicked")
                        selection = enquiry
                    })
            }}
        }
    }
    

    【讨论】:

    • 如果我做出改变,我会得到 Initializer 'init(_:id:selection:rowContent:)' 要求 'FetchRequest' 符合 'RandomAccessCollection'。有什么建议吗?
    • 这不起作用。我不得不在 List 中使用 WrappedValue 来编译它,但是它没有显示任何行。恢复到 ForEach(没有选择)确实显示了行。
    • 我没有你的所有代码,所以无法测试,请尝试更新 - 结合 List 和 ForEach,如果后者适合你。
    • 在 ForEach 周围添加列表会导致不再显示行。