UICalendarView选择未来108天以后日期时崩溃问题求助
问题:UICalendarView添加装饰时,未来超过108天的日期导致崩溃
我正在开发一款预测未来特定日期的App,在为UICalendarView添加装饰时遇到问题。当选择未来超过108天的日期,或为该类日期添加日历装饰时,App会崩溃。
报错日志
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempted to reconfigure item identifier that does not exist in the snapshot: <_UIDatePickerCalendarDay: 0x600000ca8b40; components: 1-2024-2-14; month: <_UIDatePickerCalendarMonth: 0x600000ca8d50; calendar: <_NSSwiftCalendar: 0x60000000d640>; components: 1-2024-2>; assignedMonth: (null)>' *** First throw call stack: ( 0 CoreFoundation 0x00000001804658a8 __exceptionPreprocess + 172 1 libobjc.A.dylib 0x000000018005c09c objc_exception_throw + 56 2 Foundation 0x0000000180cf3a5c _userInfoForFileAndLine + 0 3 UIKitCore 0x00000001046abe40 -[__UIDiffableDataSourceSnapshot _validateReloadUpdateThrowingIfNeeded:] + 436 4 UIKitCore 0x00000001046abad4 -[__UIDiffableDataSourceSnapshot _commitUpdateAtomic:] + 100 5 UIKitCore 0x00000001046ab0a8 -[__UIDiffableDataSourceSnapshot reconfigureItemsWithIdentifiers:] + 76 6 UIKitCore 0x00000001046db21c -[UICalendarView reloadDecorationsForDateComponents:animated:] + 132 7 DJTimeOff 0x0000000102f18d74 $s9DJTimeOff14DJCalendarViewV12updateUIView_7contextySo010UICalendarD0C_7SwiftUI0F20RepresentableContextVyACGtF + 1208 8 DJTimeOff 0x0000000102f1b430 $s9DJTimeOff14DJCalendarViewV7SwiftUI19UIViewRepresentableAadEP06updateG0_7contexty0G4TypeQz_AD0gH7ContextVyxGtFTW + 12 9 SwiftUI 0x000000010a19a5ac OUTLINED_FUNCTION_9 + 163208 10 SwiftUI 0x000000010a0e611c OUTLINED_FUNCTION_71 + 16892 11 SwiftUI 0x000000010a0e603c OUTLINED_FUNCTION_71 + 16668 12 SwiftUI 0x000000010a0c6b68 OUTLINED_FUNCTION_11 + 63844 13 SwiftUI 0x000000010a0e5ed4 OUTLINED_FUNCTION_71 + 16308 14 SwiftUI 0x000000010a0e5cec OUTLINED_FUNCTION_71 + 15820 15 SwiftUI 0x000000010a0e4f78 OUTLINED_FUNCTION_71 + 12376 16 SwiftUI 0x0000000109c6e1c0 __swift_allocate_boxed_opaque_existential_1Tm + 208 17 AttributeGraph 0x000000019e9fa750 _ZN2AG5Graph11UpdateStack6updateEv + 504 18 AttributeGraph 0x000000019e9faee0 _ZN2AG5Graph16update_attributeENS_4data3ptrINS_4NodeEEEj + 432 19 AttributeGraph 0x000000019ea088d0 _ZN2AG8Subgraph6updateEj + 828 20 SwiftUI 0x0000000109ff7418 OUTLINED_FUNCTION_0 + 10148 21 SwiftUI 0x000000010a0c45a4 OUTLINED_FUNCTION_11 + 54176 22 SwiftUI 0x000000010a0beec0 OUTLINED_FUNCTION_11 + 31932 23 SwiftUI 0x000000010a377578 OUTLINED_FUNCTION_39 + 34700 24 SwiftUI 0x000000010a3775bc OUTLINED_FUNCTION_39 + 34768 25 UIKitCore 0x000000010512ab1c -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1492 26 QuartzCore 0x00000001884d3dd4 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 440 27 QuartzCore 0x00000001884deb30 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 124 28 QuartzCore 0x00000001883fa3bc _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 460 29 QuartzCore 0x0000000188429bf0 _ZN2CA11Transaction6commitEv + 652 30 UIKitCore 0x0000000104c30504 __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 32 31 CoreFoundation 0x00000001803c6464 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20 32 CoreFoundation 0x00000001803c5bac __CFRunLoopDoBlocks + 352 33 CoreFoundation 0x00000001803c0450 __CFRunLoopRun + 788 34 CoreFoundation 0x00000001803bfd28 CFRunLoopRunSpecific + 572 35 GraphicsServices 0x000000018986ebc0 GSEventRunModal + 160 36 UIKitCore 0x0000000104c17fdc -[UIApplication _run] + 868 37 UIKitCore 0x0000000104c1bc54 UIApplicationMain + 124 38 SwiftUI 0x0000000109af8524 OUTLINED_FUNCTION_70 + 500 39 SwiftUI 0x0000000109af83c4 OUTLINED_FUNCTION_70 + 148 40 SwiftUI 0x000000010980a108 OUTLINED_FUNCTION_2 + 92 41 DJTimeOff 0x0000000102f15db4 $s9DJTimeOff0aB3AppV5$mainyyFZ + 40 42 DJTimeOff 0x0000000102f16340 main + 12 43 dyld 0x00000001030f9558 start_sim + 20 44 ??? 0x00000001032f9f28 0x0 + 4348419880 45 ??? 0xaf6e800000000000 0x0 + 12641181941563916288 ) libc++abi: terminating due to uncaught exception of type NSException
相关代码
设置日期的方法
func setDaysOff(startDate:Date, numberOfDays:Int) { self.changedDayOff = nil daysOff.removeAll() let newday = DayOff(firstDay: startDate, dayIcon: .firstDay) let newday2 = DayOff(firstDay: startDate.diff(numDays: 1), dayIcon: .secondDay) let newday3 = DayOff(firstDay: startDate.diff(numDays: 2), dayIcon: .thirdDay) daysOff.append(newday) daysOff.append(newday2) daysOff.append(newday3) for dy in 1...numberOfDays - 1 { let newDay = DayOff(firstDay: startDate.diff(numDays: 9 * dy),dayIcon: .firstDay) let newday2 = DayOff(firstDay: startDate.diff(numDays: (9 * dy) + 1), dayIcon: .secondDay) let newday3 = DayOff(firstDay: startDate.diff(numDays: (9 * dy) + 2), dayIcon: .thirdDay) daysOff.append(newDay) daysOff.append(newday2) daysOff.append(newday3) } changedDayOff = newday }
更新日历装饰的方法
func updateUIView(_ uiView: UICalendarView, context: Context) { if DS.changedDayOff != nil { for te in 0..<DS.daysOff.count { uiView.reloadDecorations(forDateComponents: [DS.daysOff[te].dateComponents], animated: true) } } }
解决方案
问题原因
UICalendarView默认的availableDateRange范围有限(通常是当前日期前后各54天,总共108天左右),当尝试为超出这个范围的日期调用reloadDecorations时,这些日期不在日历当前的数据源快照中,就会触发NSInternalInconsistencyException崩溃。
解决办法
1. 过滤超出可用范围的日期
在调用reloadDecorations前,先过滤掉不在UICalendarView可用范围内的日期组件:
func updateUIView(_ uiView: UICalendarView, context: Context) { guard DS.changedDayOff != nil else { return } // 获取日历的可用日期范围,默认用当前日期前后1年作为 fallback let availableRange = uiView.availableDateRange ?? DateInterval( start: Date(), end: Date().addingTimeInterval(60*60*24*365) ) // 过滤出有效日期组件 let validComponents = DS.daysOff.compactMap { dayOff in guard let date = Calendar.current.date(from: dayOff.dateComponents), availableRange.contains(date) else { return nil } return dayOff.dateComponents } if !validComponents.isEmpty { uiView.reloadDecorations(forDateComponents: validComponents, animated: true) } }
2. 扩大UICalendarView的可用日期范围
在初始化UICalendarView时,设置覆盖所有需要添加装饰的日期范围:
func makeUIView(context: Context) -> UICalendarView { let calendarView = UICalendarView() // 设置覆盖未来1年的可用范围,可根据实际需求调整 let startDate = Date() let endDate = startDate.addingTimeInterval(60*60*24*365) calendarView.availableDateRange = DateInterval(start: startDate, end: endDate) // 其他日历配置(如选择样式、装饰代理等) return calendarView }
内容的提问来源于stack exchange,提问作者DonjaMan
相关产品推荐
相关产品推荐

