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

SwiftUI中Foreach绑定Sheet在索引4后无法弹出求助

问题分析与解决方案

核心问题原因

你的问题出在LazyVStack的懒加载特性与Sheet修饰符的作用范围上。每个CalendarMonthView都自带.sheet修饰符,而LazyVStack会在视图滚出屏幕时销毁对应的子视图(比如4月及以后的月份视图)。当你滚动回去点击日期气泡时,虽然isSheetPresenting被设为true,但由于视图可能被重新初始化,SwiftUI的视图状态跟踪出现异常,导致Sheet无法正确弹出。

修复步骤

将Sheet的管理逻辑从子视图CalendarMonthView移到父视图YearView中,让父视图统一处理Sheet的展示:

  1. 修改YearView,添加用于控制Sheet的状态变量:
struct YearView: View {
    @State private var createSheetDate: Date?
    @State private var editSheetDay: CalendarDay?
    
    var body: some View {
        ScrollViewReader { value in
            ScrollView {
                LazyVStack(alignment: .leading,spacing: 0, pinnedViews: [.sectionHeaders]) {
                    ForEach(0..<12) { val in
                        Section(header: SectionHeaderView(month: val + 1)) {
                            CalendarMonthView(
                                month: val + 1, 
                                year: getYearNumber(date: Date.now),
                                onCreateTap: { date in
                                    createSheetDate = date
                                },
                                onEditTap: { day in
                                    editSheetDay = day
                                }
                            )
                            .padding()
                        }
                    }
                }
            }
        }
        .sheet(item: $createSheetDate) { date in
            DayCreateView(date: .constant(date))
        }
        .sheet(item: $editSheetDay) { day in
            DayEditView(day: day)
        }
        .navigationBarTitle(Text(""), displayMode: .inline)
    }
}
  1. 修改CalendarMonthView,移除内部的Sheet修饰符,通过回调将点击事件传递给父视图:
struct CalendarMonthView: View {
    @Query(sort: \CalendarDay.date) var days: [CalendarDay]
    var month: Int
    var year: Int
    let onCreateTap: (Date) -> Void
    let onEditTap: (CalendarDay) -> Void
    
    init(month: Int, year: Int, onCreateTap: @escaping (Date) -> Void, onEditTap: @escaping (CalendarDay) -> Void) {
        self.month = month
        self.year = year
        self.onCreateTap = onCreateTap
        self.onEditTap = onEditTap
        _days = Query(
            filter: #Predicate<CalendarDay> { day in
                day.monthNumber == month && day.yearNumber == year
            },
            sort: \CalendarDay.date
        )
    }
    
    var body: some View {
        VStack {
            LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 7), spacing: 20) {
                ForEach(fetchDatesForMonth(month: month)) { value in
                    let savedDay = days.first(where: { $0.date == value.date })
                    ZStack {
                        if value.date < getMaxDate() {
                            Button(action: {
                                handleBubbleTap(savedDay, value.date)
                            }){
                                DayBubbleView(date: value.date, color: getBubbleColor(day: savedDay), opacity: getBubbleOpacity(day: savedDay))
                            }
                            .buttonStyle(PlainButtonStyle())
                        } else {
                            Text("")
                        }
                    }
                    .frame(width: 32, height: 32)
                }
            }
        }
    }
    
    func handleBubbleTap(_ savedDay: CalendarDay?, _ date: Date) {
        if let savedDay = savedDay {
            onEditTap(savedDay)
        } else {
            onCreateTap(date)
        }
    }
    
    // 保留你原有的fetchDatesForMonth、generateDatesForMonth、getMaxDate等方法
}

额外优化建议

  • 移除CalendarMonthView中不再需要的@State变量(selectedDate、selectedSavedDate、isSheetPresenting),减少不必要的状态管理。
  • 确保CalendarDay类型遵循Identifiable协议(或者为.sheet(item:)提供id参数),否则Sheet可能无法正确识别要展示的编辑项。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 15:00:03