0

父视图向子视图发送 aFetchRequest和 a的谓词过滤字符串Binding以返回FetchedResults计数。

过滤器字符串是@State父视图的属性。对父项中过滤器字符串的更改会导致子项更新FetchRequest.

如何让子视图更新Binding它收到的新FetchedResults计数?

请参阅代码注释以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 }
        }
    }
}
4

1 回答 1

0

我需要做两件事,以使您的代码正常工作。首先,当您更改父视图中的过滤器时,您需要更新子视图中的过滤器。这是在我的代码中使用传统绑定完成的。然后,由于 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()
        }
    }
}

  
于 2021-08-06T00:18:00.663 回答