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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 23:47:04