如何在SwiftUI列表中显示领域结果?

时间:2019-06-23 01:47:31

标签: swift realm swiftui

我已经能够将数据保存在Realm数据库中,但是无法在SwiftUI List中显示结果。

我知道我有数据,并且在控制台中打印结果没有问题。

是否可以将领域Result转换为可以在SwiftUI List上显示的格式?

import SwiftUI
import RealmSwift
import Combine

class Dog: Object {
    @objc dynamic var name = ""
    @objc dynamic var age = 0

    override static func primaryKey() -> String? {
        return "name"
    }
}

class SaveDog {
    func saveDog(name: String, age: String) {
        let dog = Dog()
        dog.age  = Int(age)!
        dog.name = name

        // Get the default Realm
        let realm = try! Realm()

     print(Realm.Configuration.defaultConfiguration.fileURL!)

        // Persist your data easily
        try! realm.write {
        realm.add(dog)
        }

        print(dog)
    }
}

class RealmResults: BindableObject {
    let didChange = PassthroughSubject<Void, Never>()

    func getRealmResults() -> String{
        let realm = try! Realm()
        var results = realm.objects(Dog.self) { didSet 
 {didChange.send(())}}
        print(results)
        return results.first!.name
    }
}

struct dogRow: View {
    var dog = Dog()
    var body: some View {
        HStack {
            Text(dog.name)
            Text("\(dog.age)")
        }
    }

}

struct ContentView : View {

    @State var dogName: String = ""
    @State var dogAge: String = ""

    let saveDog = SaveDog()
    @ObjectBinding var savedResults = RealmResults()
    let realm = try! Realm()

    let dogs = Dog()

    var body: some View {
        VStack {
            Text("Hello World")
            TextField($dogName)
            TextField($dogAge)
            Button(action: {
                self.saveDog.saveDog(name: self.dogName, 
                age:self.dogAge)
//                self.savedResults.getRealmResults()
            }) {
                Text("Save")
            }
            //insert list here to show realm data

            List(0 ..< 5) { 
             item in
                Text(self.savedResults.getRealmResults())
            } //Displays the same thing 5 times
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

某些代码可能没有意义,因为我正在尝试几种方法来查看是否有任何效果。

例如,此行将在列表视图中显示结果。

return results.first!.name

如果我只是返回结果,则列表文本视图中什么也不会显示。

正如我在下面评论的那样,我将在有空的时候尝试使用ForEach方法。看起来很有希望。

4 个答案:

答案 0 :(得分:4)

您在ListForEach中传递的数据必须符合Identifiable协议。

您可以在Realm模型中采用它,也可以使用.identified(by:)方法。


即使这样,如果数据更改,View也不会重新加载。

您可以包装Results并将其设置为BindableObject,以便视图可以检测到更改并重新加载自身:

class BindableResults<Element>: BindableObject where Element: RealmSwift.RealmCollectionValue {

    let didChange = PassthroughSubject<Void, Never>()

    let results: Results<Element>
    private var token: NotificationToken!

    init(results: Results<Element>) {
        self.results = results
        lateInit()
    }

    func lateInit() {
        token = results.observe { _ in
            self.didChange.send(())
        }
    }

    deinit {
        token.invalidate()
    }
}

并像这样使用它:

struct ContentView : View {

    @ObjectBinding var dogs = BindableResults(results: try! Realm().objects(Dog.self))

    var body: some View {
        List(dogs.results.identified(by: \.name)) { dog in
            DogRow(dog: dog)
        }
    }

}

答案 1 :(得分:2)

我创建了一个通用解决方案来显示和添加/删除任何Results<T>。默认情况下,Results<T>是“活动”的。当@Published属性 WILL 更新时,SwiftUI会将更改发送到View。收到RealmCollectionChange<Results<T>>通知时,Results<T>已经更新;因此,由于索引超出范围,在删除时将发生fatalError。相反,我使用“实时” Results<T>来跟踪更改,并使用“冻结” Results<T>与View一起使用。可以在此处找到完整的工作示例,包括如何将通用ViewRealmViewModel<T>一起使用(如下所示):SwiftUI+Realmenum Status用于显示ProgressView,“未找到记录”等(如适用),如项目所示。另外,请注意,当需要计数或单个对象时,将使用“冻结” Results<T>。删除时,IndexSet的{​​{1}}将返回“冻结” onDelete中的位置,以便检查对象是否仍存在于“活动” Results<T>中。

Results<T>

}

答案 2 :(得分:1)

这是最直接的方法:

struct ContentView: View {
    @State private var dog: Results<Dog> = try! Realm(configuration: Realm.Configuration(schemaVersion: 1)).objects(Dog.self)

    var body: some View {
        ForEach(dog, id: \.name) { i in
        Text(String((i.name)!))
        }
    }
}

...就这样,它就可以了!

答案 3 :(得分:0)

这是使用新的Realm Frozen()集合的另一个选项。虽然这是早期阶段,但在将“资产”添加到数据库时,UI会自动更新。在此示例中,它们是从NSOperation线程添加的,该线程应该是后台线程。

在此示例中,边栏基于数据库中的不同值列出了不同的属性组-请注意,您可能希望以更可靠的方式实现此功能-但作为快速POC,它可以很好地工作。参见下图。

struct CategoryBrowserView: View {
    @ObservedObject var assets: RealmSwift.List<Asset> = FileController.shared.assets
    @ObservedObject var model = ModelController.shared
    
    @State private var searchTerm: String = ""
    @State var isEventsShowing: Bool = false
    @State var isProjectsShowing: Bool = false
    @State var isLocationsShowing: Bool = false
    
    var projects: Results<Asset> {
        return assets.sorted(byKeyPath: "project").distinct(by: ["project"])
    }
    var events: Results<Asset> {
        return assets.sorted(byKeyPath: "event").distinct(by: ["event"])
    }
    var locations: Results<Asset> {
        return assets.sorted(byKeyPath: "location").distinct(by: ["location"])
    }
    @State var status: Bool = false
    
    var body: some View {
        VStack(alignment: .leading) {
        ScrollView {
            VStack(alignment: .leading) {
                
                // Projects
                DisclosureGroup(isExpanded: $isProjectsShowing) {
                    
                    VStack(alignment:.trailing, spacing: 4) {
                        
                        ForEach(filteredProjectsCollection().freeze()) { asset in
                            HStack {
                                    Text(asset.project)
                                    Spacer()
                                Image(systemName: self.model.selectedProjects.contains(asset.project) ? "checkmark.square" : "square")
                                        .resizable()
                                        .frame(width: 17, height: 17)
                                    .onTapGesture { self.model.addProject(project: asset.project) }
                            }
                        }
                    }.frame(maxWidth:.infinity)
                    .padding(.leading, 20)
                    
                } label: {
                    HStack(alignment:.center) {
                        Image(systemName: "person.2")
                        Text("Projects").font(.system(.title3))
                        Spacer()
                    }.padding([.top, .bottom], 8).foregroundColor(.secondary)
                }
                
                // Events
                DisclosureGroup(isExpanded: $isEventsShowing) {
                    
                    VStack(alignment:.trailing, spacing: 4) {
                        
                        ForEach(filteredEventsCollection().freeze()) { asset in
                            HStack {
                             Text(asset.event)
                                Spacer()
                            Image(systemName: self.model.selectedEvents.contains(asset.event) ? "checkmark.square" : "square")
                                    .resizable()
                                    .frame(width: 17, height: 17)
                                .onTapGesture { self.model.addEvent(event: asset.event) }
                            }
                        }
                    }.frame(maxWidth:.infinity)
                    .padding(.leading, 20)
                    
                } label: {
                    HStack(alignment:.center) {
                        Image(systemName: "calendar")
                        Text("Events").font(.system(.title3))
                        Spacer()
                    }.padding([.top, .bottom], 8).foregroundColor(.secondary)
                }
                
                // Locations
                DisclosureGroup(isExpanded: $isLocationsShowing) {
                    
                    VStack(alignment:.trailing, spacing: 4) {
                        
                        ForEach(filteredLocationCollection().freeze()) { asset in
                            HStack {
                             Text(asset.location)
                                Spacer()
                            Image(systemName: self.model.selectedLocations.contains(asset.location) ? "checkmark.square" : "square")
                                    .resizable()
                                    .frame(width: 17, height: 17)
                                .onTapGesture { self.model.addLocation(location: asset.location) }
                            }
                        }
                    }.frame(maxWidth:.infinity)
                    .padding(.leading, 20)
                    
                } label: {
                    HStack(alignment:.center) {
                        Image(systemName: "flag")
                        Text("Locations").font(.system(.title3))
                        Spacer()
                    }.padding([.top, .bottom], 8).foregroundColor(.secondary)
                }
                
            }.padding(.all, 10)
            .background(Color(NSColor.controlBackgroundColor))
        }
            SearchBar(text: self.$searchTerm)
                .frame(height: 30, alignment: .leading)
        }
    }
    
    func filteredProjectsCollection() -> AnyRealmCollection<Asset> {
        if self.searchTerm.isEmpty {
            return AnyRealmCollection(self.projects)
        } else {
            return AnyRealmCollection(self.projects.filter("project CONTAINS[c] %@ || event CONTAINS[c] %@ || location CONTAINS[c] %@ || tags CONTAINS[c] %@", searchTerm, searchTerm, searchTerm, searchTerm))
        }
    }
    func filteredEventsCollection() -> AnyRealmCollection<Asset> {
        if self.searchTerm.isEmpty {
            return AnyRealmCollection(self.events)
        } else {
            return AnyRealmCollection(self.events.filter("project CONTAINS[c] %@ || event CONTAINS[c] %@ || location CONTAINS[c] %@ || tags CONTAINS[c] %@", searchTerm, searchTerm, searchTerm, searchTerm))
        }
    }
    func filteredLocationCollection() -> AnyRealmCollection<Asset> {
        if self.searchTerm.isEmpty {
            return AnyRealmCollection(self.locations)
        } else {
            return AnyRealmCollection(self.locations.filter("project CONTAINS[c] %@ || event CONTAINS[c] %@ || location CONTAINS[c] %@ || tags CONTAINS[c] %@", searchTerm, searchTerm, searchTerm, searchTerm))
        }
    }
    func filteredCollection() -> AnyRealmCollection<Asset> {
        if self.searchTerm.isEmpty {
            return AnyRealmCollection(self.assets)
        } else {
            return AnyRealmCollection(self.assets.filter("project CONTAINS[c] %@ || event CONTAINS[c] %@ || location CONTAINS[c] %@ || tags CONTAINS[c] %@", searchTerm, searchTerm, searchTerm, searchTerm))
        }
    }
    func delete(at offsets: IndexSet) {
        if let realm = assets.realm {
            try! realm.write {
                realm.delete(assets[offsets.first!])
            }
        } else {
            assets.remove(at: offsets.first!)
        }
    }
    
}

struct CategoryBrowserView_Previews: PreviewProvider {
    static var previews: some View {
        CategoryBrowserView()
    }
}

struct CheckboxToggleStyle: ToggleStyle {
    func makeBody(configuration: Configuration) -> some View {
        return HStack {
            configuration.label
            Spacer()
            Image(systemName: configuration.isOn ? "checkmark.square" : "square")
                .resizable()
                .frame(width: 22, height: 22)
                .onTapGesture { configuration.isOn.toggle() }
        }
    }
}

enter image description here

相关问题