Return 当@FetchRequest 谓词过滤器更改时,SwiftUI @FetchedResults 从子视图计数到父视图
Return SwiftUI @FetchedResults count from child view to parent when @FetchRequest predicate filter changes
父视图向子视图发送 FetchRequest
和 Binding
的谓词过滤器字符串到 return FetchedResults
计数。
过滤器字符串是父视图的@State
属性。更改父项中的过滤器字符串会导致子项更新 FetchRequest
.
我怎样才能让子视图用新的 FetchedResults
计数更新它收到的 Binding
?
请参阅 Child
中的代码注释了解我的尝试。
struct Parent: View {
@State private var filter = "" // Predicate filter
@State private var fetchCount = 0 // To be updated by child
var body: some View {
VStack {
// Create core data test records (Entity "Item" with a property named "name")
Button("Create Test Items") {
let context = PersistenceController.shared.container.viewContext
let names = ["a", "ab", "aab", "b"]
for name in names {
let item = Item(context: context)
item.name = name
try! context.save()
}
}
// Buttons to modify the filter to update the fetch request
HStack {
Button("Add") {
filter = filter + "a"
}
Button("Remove") {
filter = String(filter.prefix(filter.count-1 >= 0 ? filter.count-1 : 0))
}
Text("Filter: \(filter)")
}
Text("Fetch count in parent view (not ok): \(fetchCount)")
Child(filter: filter, fetchCount: $fetchCount)
Spacer()
}
}
}
struct Child: View {
@FetchRequest var fetchRequest: FetchedResults<Item>
@Binding var fetchCount: Int
init(filter: String, fetchCount: Binding<Int>) {
let predicate = NSPredicate(format: "name BEGINSWITH[c] %@", filter)
self._fetchRequest = FetchRequest<Item>(
entity: Item.entity(),
sortDescriptors: [],
predicate: filter.isEmpty ? nil : predicate
)
self._fetchCount = fetchCount
// self.fetchCount = fetchRequest.count // "Modifying state during view updates, this will cause undefined behavior"
}
var body: some View {
VStack {
Text("Fetch count in child view (ok): \(fetchRequest.count)")
.onAppear { fetchCount = fetchRequest.count } // Fires once, not whenever the predicate filter changes
// The ForEach's onAppear() doesn't update the count when the results are reduced and repeatedly updates for every new item in results
ForEach(fetchRequest) { item in
Text(item.name ?? "nil")
}
//.onAppear { fetchCount = fetchRequest.count }
}
}
}
为了使您的代码正常工作,我需要做两件事。
首先,当您更改父视图中的过滤器时,您需要更新子视图中的过滤器。这个
使用传统的绑定在我的代码中完成。然后,由于 onAppear 只发生一次,
我使用 onReceive 来更新 fetchCount。这是我的代码:
编辑:根据@Jesse Blake 的建议
import Foundation
import SwiftUI
struct Child: View {
@FetchRequest var fetchRequest: FetchedResults<Item>
@Binding var fetchCount: Int
var filter: String // <-- here
init(filter: String, fetchCount: Binding<Int>) {
self.filter = filter // <-- here
let predicate = NSPredicate(format: "name BEGINSWITH[c] %@", filter)
self._fetchRequest = FetchRequest<Item>(
entity: Item.entity(),
sortDescriptors: [],
predicate: filter.isEmpty ? nil : predicate
)
self._fetchCount = fetchCount
}
var body: some View {
VStack {
Text("Fetch count in child view (ok): \(fetchRequest.count)")
ForEach(fetchRequest) { item in
Text(item.name ?? "nil")
}
}
// in older ios
// .onChange(of: fetchRequest.count) { newVal in // <-- here
// fetchCount = fetchRequest.count
// }
.onReceive(fetchRequest.publisher.count()) { _ in // <-- here
fetchCount = fetchRequest.count
}
}
}
struct Parent: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var filter = "" // Predicate filter
@State private var fetchCount = 0 // To be updated by child
var body: some View {
VStack (spacing: 50) {
// Create core data test records (Entity "Item" with a property named "name")
Button("Create Test Items") {
let context = PersistenceController.shared.container.viewContext
let names = ["a", "ab", "aab", "b"]
for name in names {
let item = Item(context: context)
item.name = name
try! context.save()
}
}
// Buttons to modify the filter to update the fetch request
HStack {
Button("Add") {
filter = filter + "a"
}
Button("Remove") {
filter = String(filter.prefix(filter.count-1 >= 0 ? filter.count-1 : 0))
}
Text("Filter: \(filter)")
}
Text("Fetch count in parent view (not ok): \(fetchCount)")
Child(filter: filter, fetchCount: $fetchCount) // <-- here
Spacer()
}
}
}
父视图向子视图发送 FetchRequest
和 Binding
的谓词过滤器字符串到 return FetchedResults
计数。
过滤器字符串是父视图的@State
属性。更改父项中的过滤器字符串会导致子项更新 FetchRequest
.
我怎样才能让子视图用新的 FetchedResults
计数更新它收到的 Binding
?
请参阅 Child
中的代码注释了解我的尝试。
struct Parent: View {
@State private var filter = "" // Predicate filter
@State private var fetchCount = 0 // To be updated by child
var body: some View {
VStack {
// Create core data test records (Entity "Item" with a property named "name")
Button("Create Test Items") {
let context = PersistenceController.shared.container.viewContext
let names = ["a", "ab", "aab", "b"]
for name in names {
let item = Item(context: context)
item.name = name
try! context.save()
}
}
// Buttons to modify the filter to update the fetch request
HStack {
Button("Add") {
filter = filter + "a"
}
Button("Remove") {
filter = String(filter.prefix(filter.count-1 >= 0 ? filter.count-1 : 0))
}
Text("Filter: \(filter)")
}
Text("Fetch count in parent view (not ok): \(fetchCount)")
Child(filter: filter, fetchCount: $fetchCount)
Spacer()
}
}
}
struct Child: View {
@FetchRequest var fetchRequest: FetchedResults<Item>
@Binding var fetchCount: Int
init(filter: String, fetchCount: Binding<Int>) {
let predicate = NSPredicate(format: "name BEGINSWITH[c] %@", filter)
self._fetchRequest = FetchRequest<Item>(
entity: Item.entity(),
sortDescriptors: [],
predicate: filter.isEmpty ? nil : predicate
)
self._fetchCount = fetchCount
// self.fetchCount = fetchRequest.count // "Modifying state during view updates, this will cause undefined behavior"
}
var body: some View {
VStack {
Text("Fetch count in child view (ok): \(fetchRequest.count)")
.onAppear { fetchCount = fetchRequest.count } // Fires once, not whenever the predicate filter changes
// The ForEach's onAppear() doesn't update the count when the results are reduced and repeatedly updates for every new item in results
ForEach(fetchRequest) { item in
Text(item.name ?? "nil")
}
//.onAppear { fetchCount = fetchRequest.count }
}
}
}
为了使您的代码正常工作,我需要做两件事。 首先,当您更改父视图中的过滤器时,您需要更新子视图中的过滤器。这个 使用传统的绑定在我的代码中完成。然后,由于 onAppear 只发生一次, 我使用 onReceive 来更新 fetchCount。这是我的代码:
编辑:根据@Jesse Blake 的建议
import Foundation
import SwiftUI
struct Child: View {
@FetchRequest var fetchRequest: FetchedResults<Item>
@Binding var fetchCount: Int
var filter: String // <-- here
init(filter: String, fetchCount: Binding<Int>) {
self.filter = filter // <-- here
let predicate = NSPredicate(format: "name BEGINSWITH[c] %@", filter)
self._fetchRequest = FetchRequest<Item>(
entity: Item.entity(),
sortDescriptors: [],
predicate: filter.isEmpty ? nil : predicate
)
self._fetchCount = fetchCount
}
var body: some View {
VStack {
Text("Fetch count in child view (ok): \(fetchRequest.count)")
ForEach(fetchRequest) { item in
Text(item.name ?? "nil")
}
}
// in older ios
// .onChange(of: fetchRequest.count) { newVal in // <-- here
// fetchCount = fetchRequest.count
// }
.onReceive(fetchRequest.publisher.count()) { _ in // <-- here
fetchCount = fetchRequest.count
}
}
}
struct Parent: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var filter = "" // Predicate filter
@State private var fetchCount = 0 // To be updated by child
var body: some View {
VStack (spacing: 50) {
// Create core data test records (Entity "Item" with a property named "name")
Button("Create Test Items") {
let context = PersistenceController.shared.container.viewContext
let names = ["a", "ab", "aab", "b"]
for name in names {
let item = Item(context: context)
item.name = name
try! context.save()
}
}
// Buttons to modify the filter to update the fetch request
HStack {
Button("Add") {
filter = filter + "a"
}
Button("Remove") {
filter = String(filter.prefix(filter.count-1 >= 0 ? filter.count-1 : 0))
}
Text("Filter: \(filter)")
}
Text("Fetch count in parent view (not ok): \(fetchCount)")
Child(filter: filter, fetchCount: $fetchCount) // <-- here
Spacer()
}
}
}