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

SwiftUI首次点击卡片弹出FullScreenCover无数据问题求助

问题原因与解决方案

核心问题

  1. 重复绑定模态视图:你在ForEach循环内给每个Button都添加了fullScreenCover,所有模态都绑定同一个showSheet状态。这会导致SwiftUI创建多份模态实例,首次触发时容易出现状态更新和视图加载的时序冲突。
  2. 状态更新时序问题:首次点击时,showSheet = true会立即触发模态展示,但selectedGoal = goal的状态更新可能还没完成,此时Modal拿到的还是初始空的Goal实例,所以显示无数据。

修复方案(推荐)

改用fullScreenCover(item:)的方式,通过可选类型的选中目标来控制模态展示,这种方式能确保模态展示时,选中的数据已经完全赋值:

import SwiftUI

struct GoalsView: View {
    
    @Binding var rootActive: Bool
    @Binding var addGoalIsClicked: Bool

    // 改为可选类型,初始为nil
    @State private var selectedGoal: Goal?
    
    var body: some View {
        
        ScrollView {
            VStack(){
                HStack{
                    Text("Add goals")
                        .fontWeight(.bold)
                        .font(.system(size: 28))
                    Spacer()
                }
                .padding()
                
                ForEach(goalDB) {goal in
                    Button() {
                        // 直接赋值选中的目标,触发模态展示
                        selectedGoal = goal
                    }label: {
                        Label(goal.title, systemImage: "")
                            .frame(maxWidth: .infinity)
                            .foregroundColor(.black)
                    }
                    .padding()
                    .background {
                        RoundedRectangle(cornerRadius: 10)
                            .frame(maxWidth: .infinity)
                            .foregroundColor(.white)
                    }
                }
                .padding(4)
                
            }
        }
        // 将fullScreenCover移到循环外部,整个视图只保留一个模态
        .fullScreenCover(item: $selectedGoal) { selectedItem in
            ModalView(rootActive: $rootActive, goal: selectedItem)
        }
        .background(Color.gray.opacity(0.15))
    }
}

// 需要让Goal遵循Identifiable协议(如果还没的话)
extension Goal: Identifiable {}

另一种修复方式(基于原状态逻辑调整)

如果不想修改状态类型,也可以把fullScreenCover移到循环外部,确保只有一个模态实例,同时通过延迟触发确保状态先更新再展示:

import SwiftUI

struct GoalsView: View {
    
    @Binding var rootActive: Bool
    @State private var showSheet = false
    @Binding var addGoalIsClicked: Bool

    @State private var selectedGoal : Goal = Goal(title: "", description: "")
    
    var body: some View {
        
        ScrollView {
            VStack(){
                HStack{
                    Text("Add goals")
                        .fontWeight(.bold)
                        .font(.system(size: 28))
                    Spacer()
                }
                .padding()
                
                ForEach(goalDB) {goal in
                    Button() {
                        selectedGoal = goal
                        // 延迟触发,确保状态更新完成后再展示模态
                        DispatchQueue.main.async {
                            showSheet = true
                        }
                    }label: {
                        Label(goal.title, systemImage: "")
                            .frame(maxWidth: .infinity)
                            .foregroundColor(.black)
                    }
                    .padding()
                    .background {
                        RoundedRectangle(cornerRadius: 10)
                            .frame(maxWidth: .infinity)
                            .foregroundColor(.white)
                    }
                }
                .padding(4)
                
            }
        }
        // 移到外部
        .fullScreenCover(isPresented: $showSheet) {
            ModalView(rootActive: $rootActive, goal: selectedGoal)
        }
        .background(Color.gray.opacity(0.15))
    }
}

说明

第一种方案(item:绑定)是SwiftUI官方推荐的方式,它直接将模态展示与选中的数据绑定,从根源上避免了时序问题,代码也更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 02:25:53