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

如何通过环境属性在SwiftUI视图间传递Core Data对象

实现无参数传递的CoreData详情页与优化预览

可行方案说明

完全可以实现你的需求:通过传递Pokemon的唯一ID而非整个对象,在详情页用@FetchRequest获取目标数据,同时预览直接使用.environment传递上下文。


步骤1:修改ContentView,传递ID而非对象

将NavigationLink的跳转值改为Pokemon的id,同时更新导航目标的接收类型:

struct ContentView: View {
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Pokemon.id, ascending: true)],
        animation: .default)
    private var pokedex: FetchedResults<Pokemon>

    var body: some View {
        NavigationStack {
            List(pokedex) { pokemon in
                // 传递Pokemon的id而非整个对象
                NavigationLink(value: pokemon.id) {
                    Text(pokemon.name!.capitalized)
                }
            }
            .navigationTitle("Pokedex")
            // 接收Int类型的id,跳转至详情页
            .navigationDestination(for: Int.self) { pokemonId in
                PokemonDetail(pokemonId: pokemonId)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
           .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

步骤2:重构PokemonDetail,通过ID获取数据

移除直接传入的pokemon属性,改为通过初始化时接收的pokemonId构建@FetchRequest,自动从环境上下文获取数据:

struct PokemonDetail: View {
    let pokemonId: Int
    @Environment(\.managedObjectContext) private var viewContext
    
    // 根据传入的id构建FetchRequest,过滤目标Pokemon
    @FetchRequest private var pokemon: FetchedResults<Pokemon>
    
    init(pokemonId: Int) {
        self.pokemonId = pokemonId
        _pokemon = FetchRequest(
            sortDescriptors: [],
            predicate: NSPredicate(format: "id == %d", pokemonId),
            animation: .default
        )
    }
    
    var body: some View {
        // 处理数据未找到的情况
        guard let targetPokemon = pokemon.first else {
            return Text("未找到该宝可梦")
                .navigationTitle("错误")
        }
        
        ZStack {
            Image(targetPokemon.background)
                .resizable()
                .scaledToFit()
                .shadow(color: .black, radius: 6, x: 0, y: 0)
            
            AsyncImage(url: targetPokemon.sprite) { image in
                image
                    .resizable()
                    .scaledToFit()
                    .shadow(color: .black, radius: 6, x: 0, y: 0)
            } placeholder: {
                ProgressView()
            }
            .padding(.top, 100)
        }
        .navigationTitle(targetPokemon.name?.capitalized ?? "宝可梦")
    }
}

步骤3:优化预览,使用.environment传递上下文

直接传入预览上下文,指定一个预览数据中存在的id即可,无需手动执行fetch:

struct PokemonDetail_Previews: PreviewProvider {
    static var previews: some View {
        NavigationStack {
            // 假设预览数据中存在id为1的Pokemon
            PokemonDetail(pokemonId: 1)
        }
        .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

关键说明

  • 环境上下文会自动从父视图向下传递,无需在ContentView中手动给PokemonDetail添加.environment(\.managedObjectContext, viewContext)。
  • 使用唯一ID传递而非整个CoreData对象,符合SwiftUI的导航最佳实践,避免对象引用带来的潜在问题。
  • 预览时只需确保传入的id在预览数据中存在即可,无需手动处理上下文和fetch逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 03:54:10