UIContextMenu 和 UITableView 问题

问题描述

我正在尝试添加一个与您在 iMessages 中可以找到的类似的 UIContextMenu。当您长按消息时,应显示带有一些选项的上下文菜单。 我使用 tableView(_:contextMenuConfigurationForRowAt:point:) 和其他方法。到目前为止一切顺利。

但是我遇到了两个我无法解决的问题:

  1. 最大的是预览的变化。当显示上下文菜单并且您收到一条新消息(这会导致 tableview 重新加载)时,预览将更改其内容。所以突然有一条与您最初选择的消息不同的消息。但我不明白为什么因为上下文菜单的 tableview 方法没有被调用......我怎么能解决这个问题? Apple Messages 停止添加新消息。但例如 Viber 仍然能够在有上下文菜单的情况下接收新消息。

  2. 我想在 tableView(_:willdisplayContextMenu:animator:) 的帮助下像 Apple 一样处理它......但还有第二个问题 - 这种方法仅适用于 iOS +14.0..!所以我怎么知道 iOS 14 之前会有上下文菜单

我将不胜感激。谢谢。

解决方法

我以某种方式解决了第一个问题。主要思想是使用快照而不是单元格的视图。这样即使 tableView 重新加载,快照也保持不变。

您必须实现这两种方法并在那里提供快照: tableView(_:previewForHighlightingContextMenuWithConfiguration:) tableView(_:previewForDismissingContextMenuWithConfiguration:)

在我的代码中它看起来像这样:

func tableView(_: UITableView,previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
        guard
            let messageId = configuration.identifier as? String,let indexPath = dataSource.indexPath(for: messageId),let cell = tableView.cellForRow(at: indexPath) as? YourCustomCell else {
            return nil
        }

        return makeTargetedPreview(cell: cell)
    }

 func makeTargetedPreview(cell: YourCustomCell) -> UITargetedPreview? {
        guard
            let previewView = cell.viewYouWantToDisplay,let snapshot = previewView.snapshotView(afterScreenUpdates: false)
        else {
            return nil
        }

        // 1. Prepare how should the view in the preview look like
        let parameters = UIPreviewParameters()
        parameters.backgroundColor = .clear
        parameters.visiblePath = UIBezierPath(roundedRect: previewView.bounds,cornerRadius: previewView.layer.cornerRadius)

        // 2. Prepare UIPreviewTarget so we can use snapshot
        let previewTarget = UIPreviewTarget(
            container: previewView,center: CGPoint(x: previewView.bounds.midX,y: previewView.bounds.midY)
        )

        // 3. Return UITargetedPreview with snapshot
        // important ! We can't use cell's previewView directly as it's getting changed when data reload happens
        return UITargetedPreview(view: snapshot,parameters: parameters,target: previewTarget)
    }

注意:previewForDismissing(...) 的实现是类似的。