As per request of commenter's of this post, this is how I set up the container view which holds the reactionView for the cell:
func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
guard
let indexPath = configuration.identifier as? IndexPath,
let cell = tableView.cellForRow(at: indexPath) as? MessageTableViewCell
else {
return nil
}
ctxMenuReactionsView = ReactionsView.setup()
ctxMenuReactionsView?.isHidden = true /* must set isHidden = false in the delegate method I mentioned in checkmark'd answer */
if let snapshot = cell.snapshotView(afterScreenUpdates: false) { /* snapshot the view of the cell for displaying in container */
snapshot.isHidden = false
snapshot.layer.cornerRadius = 10
snapshot.layer.masksToBounds = true
snapshot.translatesAutoresizingMaskIntoConstraints = false
/* create the container that the snapshot and reactionView will be in */
let container = UIView(frame: CGRect(
origin: .zero,
size: CGSize(
width: cell.bounds.width,
height: cell.bounds.height + ctxMenuReactionsView!.bounds.height + 5
)
))
container.backgroundColor = .clear
container.addSubview(ctxMenuReactionsView!)
container.addSubview(snapshot)
/* set up constraints for snapshot and ReactionsView */
ctxMenuReactionsView?.leftAnchor.constraint(equalTo: container.leftAnchor).isActive = true
ctxMenuReactionsView?.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
ctxMenuReactionsView?.widthAnchor.constraint(...)
container.addConstraints([
...
])
var centerPoint = CGPoint(x: cell.center.x, y: cell.center.y - ctxMenuReactionsView!.bounds.height)
let windowHeight = self.view.window?.size.height ?? 0
if snapshot.bounds.height > (windowHeight * 0.9) { /* if the snapshot of cell if too tall, we use this center point to make it fit */
centerPoint = CGPoint(x: cell.center.x, y: tableView.center.y)
}
let previewTarget = UIPreviewTarget(container: tableView, center: centerPoint)
/* makes sure background of the container is clear */
let parameters = UIPreviewParameters()
parameters.backgroundColor = .clear
if #available(iOS 14.0, *) { /* this removes the shadow from the container */
parameters.shadowPath = UIBezierPath()
}
return UITargetedPreview(view: container, parameters: parameters, target: previewTarget)
}
Be sure to pass in the indexPath of the cell in the func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) - UIContextMenuConfiguration? function in the identifier paramater like so:
return UIContextMenuConfiguration(identifier: indexPath as NSCopying,
previewProvider: nil,
actionProvider:
{
... set up menu items code
}