Swift长按上下文菜单UITargetedPreview切换视图时App崩溃问题
解决动态更新上下文菜单时的UITargetedPreview崩溃问题
问题场景
动态修改上下文菜单项时,抛出NSInternalInconsistencyException异常,错误提示:
This UITargetedPreview initializer requires that the view is in a window, but it is not. Either fix that, or use the other initializer that takes a target.
此时上下文菜单会显示为空白。
解决方案
1. 检查视图窗口状态后创建预览
在生成UITargetedPreview前先确认目标视图已挂载到窗口,无效时切换到带target参数的初始化器:
func makeTargetedPreview(for view: UIView) -> UITargetedPreview { guard view.window != nil else { let target = UIPreviewTarget(container: self.view.window!, center: view.center) return UITargetedPreview(view: view, target: target) } return UITargetedPreview(view: view) }
2. 延迟执行菜单更新逻辑
如果是在视图布局刚完成(比如cell复用、UI刷新后)触发菜单更新,用主队列延迟执行,确保视图已加入窗口层级:
func updateContextMenu() { DispatchQueue.main.async { self.collectionView.reloadItems(at: [self.currentIndexPath]) } }
3. 始终使用带target参数的初始化器
直接跳过对视图window状态的依赖,显式定义预览的容器和中心点,彻底规避断言问题:
func contextMenuConfigurationForItemAt(indexPath: IndexPath) -> UIContextMenuConfiguration? { let cell = collectionView.cellForItem(at: indexPath)! return UIContextMenuConfiguration(identifier: nil, previewProvider: { let previewView = UIView() let target = UIPreviewTarget(container: self.view.window!, center: cell.convert(cell.center, to: self.view.window)) return UITargetedPreview(view: previewView, target: target) }, actionProvider: { _ in return UIMenu(title: "", children: self.dynamicMenuActions()) }) }
4. 避免菜单展示中强制刷新
如果菜单已经弹出,不要直接强制刷新菜单项,建议先关闭菜单,更新数据源后再重新触发菜单展示,防止视图状态不一致。
问题原因
崩溃核心是UITargetedPreview的默认初始化器会断言视图必须处于窗口层级中,动态更新菜单时,若视图还未完成窗口挂载(比如cell刚复用、UI未完成布局),就会触发断言失败,导致菜单空白并崩溃。
内容的提问来源于stack exchange,提问作者Nirali Shaha
相关产品推荐
相关产品推荐

