添加一对多关系而不更新 UI

发布于 2025-01-15 21:42:33 字数 628 浏览 3 评论 0原文

我在 plan 的核心数据中有一对多关系 -> 食谱plan.recipes 的类型为 NSSet?,因此我创建了带有计算属性的自定义 NSManagedObject 类,以将它们转换为数组,并添加了一个额外的属性 recipesArray >:

public var recipesArray: [Recipe] {
    let set = recipes as? Set<Recipe> ?? []
    
    return set.sorted {
        $0.wrappedName < $1.wrappedName
    }
}

然后,我使用 ForEach 在视图中显示此列表,并使用 recipesArray 属性。该视图的子视图调用 plan.addToRecipes(recipe: Recipe) 来向关系添加新对象。然后我保存。

问题是,父视图中的 ForEach 不会对此添加做出反应。如果我通过导航刷新视图,则会显示新菜谱,但添加新菜谱时视图不会自动更新。

有谁知道该怎么做?我应该使用原始的 recipes 属性而不是这个自定义数组属性吗?

I have a one-to-many relationship in core data of plan -> recipe. plan.recipes is of type NSSet?, so I have created custom NSManagedObject classes with computed properties to convert these into arrays, adding an extra property recipesArray:

public var recipesArray: [Recipe] {
    let set = recipes as? Set<Recipe> ?? []
    
    return set.sorted {
        $0.wrappedName < $1.wrappedName
    }
}

I then display this list in a View using a ForEach, using the recipesArray property. A subview of this view calls plan.addToRecipes(recipe: Recipe), to add a new object to the relationship. I then save.

The issue is, the ForEach in the parent view does not react to this addition. If I refresh the view by navigating away, then the new recipe is shown, but the View is not automatically updated when the new recipe is added.

Does anyone know how to do this? Should I be using the original recipes property instead of this custom array one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

习ぎ惯性依靠 2025-01-22 21:42:34

您需要使用等于给定计划的谓词为Recipe创建另一个FetchRequest,例如:

struct RecipesView: View {
    var fetchRequest: FetchRequest<Recipe>
    var recipes: FetchedResults<Recipe> { fetchRequest.wrappedValue }

    init(plan: Plan) {
        let sortDescriptors = ...
        let predicate = NSPredicate(format: "plan = %@", plan)
        fetchRequest = FetchRequest(sortDescriptors: sortDescriptors, predicate: predicate, animation: .default)
    }

    var body: some View {
        ForEach(recipes) { recipe in
            RecipeView(recipe: recipe) // it's important to not access recipe's properties (firing a fault) until inside a sub-view's body.
        }
    }
}

注意:当前有一个FetchRequest 中的错误导致主体始终被调用,即使此 View 使用相同的 plan 进行初始化,因此也是相同的 FetchRequest >。这是因为 FetchRequest 结构每次都会在其中初始化一个新对象,导致它以及该 View 显示为更改为 SwiftUI。我报告了此错误,希望他们能修复它。同时,您可以使用包装器 View 来解决这个问题,该包装器将 plan 作为 let,这样主体就不会被调用。

You need to make another FetchRequest for Recipes using a predicate that equals a given plan, e.g. something like this:

struct RecipesView: View {
    var fetchRequest: FetchRequest<Recipe>
    var recipes: FetchedResults<Recipe> { fetchRequest.wrappedValue }

    init(plan: Plan) {
        let sortDescriptors = ...
        let predicate = NSPredicate(format: "plan = %@", plan)
        fetchRequest = FetchRequest(sortDescriptors: sortDescriptors, predicate: predicate, animation: .default)
    }

    var body: some View {
        ForEach(recipes) { recipe in
            RecipeView(recipe: recipe) // it's important to not access recipe's properties (firing a fault) until inside a sub-view's body.
        }
    }
}

Note: There is currently a bug in FetchRequest that results in body always invoked even when this View is init with the same plan and thus same FetchRequest. This is because the FetchRequest struct inits a new object inside it every time, causing it and consequently this View to appear as changed to SwiftUI. I reported this bug so hopefully they fix it. You could workaround it in the meantime with a wrapper View that takes the plan as a let so body won't be called.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文