You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中List嵌套ForEach无法显示数据问题求助

问题:SwiftUI中List内的ForEach无法展示Core Data数据,VStack中却可以

我开发了一个SwiftUI视图,通过FetchRequest从Core Data获取数据并存储在navigations变量中。尝试在List组件内用ForEach遍历展示navigations内容,这种写法在其他视图里多次成功,但这里就是不显示数据。把完全一样的遍历代码放到VStack里却能正常展示。我肯定漏了某个简单细节,但一直找不到。

相关代码

import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [SortDescriptor(\.date)],
        predicate: NSPredicate(value: false),
        animation: .default)
    private var navigations:FetchedResults<NavEntry>
    
    var event: Event
    
    var body: some View {
        
        ScrollView{
            let _ = updatePredicate()
            
            VStack {
                Text("ForEach inside a VStack:")
                VStack(alignment: .leading) {
                    ForEach(navigations) {nav in
                        Text("\(longTime(date: nav.date)) \(nav.note)")
                    }
                }
                Text("\n ForEach inside a List:")
                List {
                    ForEach(navigations) {nav in
                        Text("\(longTime(date: nav.date)) \(nav.note)")
                    }
                }
            }
        }
    }
    
    func updatePredicate() {
        navigations.nsPredicate = NSPredicate(format: "event == %@", event)
    }
}

输出截图

输出截图


解决方法

问题出在你直接在body里调用updatePredicate()修改FetchRequest谓词的方式,这种写法会导致视图更新时机不匹配,List对数据源变更的感知逻辑和VStack不同,无法正确响应FetchedResults的更新。

推荐的正确写法是把FetchRequest改成动态计算属性,依赖传入的event参数,这样能自动响应event变化并更新数据源:

修改后的代码

import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    var event: Event
    
    // 基于event创建动态FetchRequest
    private var navigations: FetchRequest<NavEntry> {
        FetchRequest(
            sortDescriptors: [SortDescriptor(\.date)],
            predicate: NSPredicate(format: "event == %@", event),
            animation: .default
        )
    }
    // 获取FetchedResults结果
    private var navigationResults: FetchedResults<NavEntry> { navigations.wrappedValue }
    
    var body: some View {
        ScrollView{
            VStack {
                Text("ForEach inside a VStack:")
                VStack(alignment: .leading) {
                    ForEach(navigationResults) { nav in
                        Text("\(longTime(date: nav.date)) \(nav.note)")
                    }
                }
                Text("\n ForEach inside a List:")
                List {
                    ForEach(navigationResults) { nav in
                        Text("\(longTime(date: nav.date)) \(nav.note)")
                    }
                }
            }
        }
    }
    
    func longTime(date: Date) -> String {
        // 补充日期格式化逻辑示例
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        formatter.timeStyle = .long
        return formatter.string(from: date)
    }
}

原因说明

  • 原来的写法中,在body渲染时才修改谓词,List无法在正确的时机感知到数据源的变化,而VStack的渲染逻辑相对宽松,能显示出数据。
  • 改成计算属性的FetchRequest后,当event参数变化时,FetchRequest会自动重建,对应的FetchedResults也会同步更新,List和VStack都能正确展示数据。

备选方案

如果一定要保留原有的@FetchRequest写法,可以把updatePredicate()移到onAppear中调用,但这种方式仅在视图首次出现时生效,当event参数变化时不会自动更新:

var body: some View {
    ScrollView{
        VStack {
            // ... 原有内容不变
        }
        .onAppear {
            updatePredicate()
        }
    }
}

内容的提问来源于stack exchange,提问作者Peel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:05:13