如何隐藏UIContextMenuInteraction/UITargetedPreview黑色背景?

How to hide UIContextMenuInteraction/UITargetedPreview black background?

我想将 UIContextMenuInteraction 添加到具有一些透明部分的 UIView。当用户与此视图交互时,iOS 以黑色背景显示此视图。

是否可以使这些部分透明或更改颜色?

终于找到解决办法了! 如果你想显示一个自定义的曲线,你需要实现这个功能:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
    let previewTarget = UIPreviewTarget(container: self.view, center: center)
    let previewParams = UIPreviewParameters()

    var topLeft : CGFloat = 8
    var topRight : CGFloat = 9
    var bottomRight : CGFloat = 10
    var bottomLeft : CGFloat = 11

    let topLeftRadius = CGSize(width: topLeft, height: topLeft)
    let topRightRadius = CGSize(width: topRight, height: topRight)
    let bottomLeftRadius = CGSize(width: bottomLeft, height: bottomLeft)
    let bottomRightRadius = CGSize(width: bottomRight, height: bottomRight)

    previewParams.visiblePath = UIBezierPath(shouldRoundRect: TheExactRectYouWantToShow, topLeftRadius: topLeftRadius, topRightRadius: topRightRadius, bottomLeftRadius: bottomLeftRadius, bottomRightRadius: bottomRightRadius)
    let targetView = UITargetedPreview(view: YourViewToShow, parameters: previewParams, target: previewTarget)
    return targetView
}

在这里,我们围绕自定义形状的视图绘制了一个 UIBezierPath。为此,您可能还需要此扩展程序:

extension UIBezierPath {
convenience init(shouldRoundRect rect: CGRect, topLeftRadius: CGSize = .zero, topRightRadius: CGSize = .zero, bottomLeftRadius: CGSize = .zero, bottomRightRadius: CGSize = .zero){

    self.init()

    let path = CGMutablePath()

    let topLeft = rect.origin
    let topRight = CGPoint(x: rect.maxX, y: rect.minY)
    let bottomRight = CGPoint(x: rect.maxX, y: rect.maxY)
    let bottomLeft = CGPoint(x: rect.minX, y: rect.maxY)

    if topLeftRadius != .zero{
        path.move(to: CGPoint(x: topLeft.x+topLeftRadius.width, y: topLeft.y))
    } else {
        path.move(to: CGPoint(x: topLeft.x, y: topLeft.y))
    }

    if topRightRadius != .zero{
        path.addLine(to: CGPoint(x: topRight.x-topRightRadius.width, y: topRight.y))
        path.addCurve(to:  CGPoint(x: topRight.x, y: topRight.y+topRightRadius.height), control1: CGPoint(x: topRight.x, y: topRight.y), control2:CGPoint(x: topRight.x, y: topRight.y+topRightRadius.height))
    } else {
         path.addLine(to: CGPoint(x: topRight.x, y: topRight.y))
    }

    if bottomRightRadius != .zero{
        path.addLine(to: CGPoint(x: bottomRight.x, y: bottomRight.y-bottomRightRadius.height))
        path.addCurve(to: CGPoint(x: bottomRight.x-bottomRightRadius.width, y: bottomRight.y), control1: CGPoint(x: bottomRight.x, y: bottomRight.y), control2: CGPoint(x: bottomRight.x-bottomRightRadius.width, y: bottomRight.y))
    } else {
        path.addLine(to: CGPoint(x: bottomRight.x, y: bottomRight.y))
    }

    if bottomLeftRadius != .zero{
        path.addLine(to: CGPoint(x: bottomLeft.x+bottomLeftRadius.width, y: bottomLeft.y))
        path.addCurve(to: CGPoint(x: bottomLeft.x, y: bottomLeft.y-bottomLeftRadius.height), control1: CGPoint(x: bottomLeft.x, y: bottomLeft.y), control2: CGPoint(x: bottomLeft.x, y: bottomLeft.y-bottomLeftRadius.height))
    } else {
        path.addLine(to: CGPoint(x: bottomLeft.x, y: bottomLeft.y))
    }

    if topLeftRadius != .zero{
        path.addLine(to: CGPoint(x: topLeft.x, y: topLeft.y+topLeftRadius.height))
        path.addCurve(to: CGPoint(x: topLeft.x+topLeftRadius.width, y: topLeft.y) , control1: CGPoint(x: topLeft.x, y: topLeft.y) , control2: CGPoint(x: topLeft.x+topLeftRadius.width, y: topLeft.y))
    } else {
        path.addLine(to: CGPoint(x: topLeft.x, y: topLeft.y))
    }

    path.closeSubpath()
    cgPath = path
}

还有这个:

extension UIView {

func roundCorners(topLeft: CGFloat = 0, topRight: CGFloat = 0, bottomLeft: CGFloat = 0, bottomRight: CGFloat = 0) {//(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat) {
    let topLeftRadius = CGSize(width: topLeft, height: topLeft)
    let topRightRadius = CGSize(width: topRight, height: topRight)
    let bottomLeftRadius = CGSize(width: bottomLeft, height: bottomLeft)
    let bottomRightRadius = CGSize(width: bottomRight, height: bottomRight)
    let maskPath = UIBezierPath(shouldRoundRect: bounds, topLeftRadius: topLeftRadius, topRightRadius: topRightRadius, bottomLeftRadius: bottomLeftRadius, bottomRightRadius: bottomRightRadius)
    let shape = CAShapeLayer()
    shape.path = maskPath.cgPath
    layer.mask = shape
}
}

更改上下文菜单背景颜色的更简单解决方案

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
    let previewTarget = UIPreviewTarget(container: self, center: <#your-view>.center)
    let previewParams = UIPreviewParameters()
    previewParams.backgroundColor = .clear

    return UITargetedPreview(view: <#your-view>, parameters: previewParams, target: previewTarget)
}

所说,您可以使用previewForHighlightingContextMenuWithConfigurationpreviewForDismissingContextMenuWithConfiguration委托方法创建您自己的UITargetedPreview并将其背景颜色设置为清除。

func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview?
{
    guard let indexPath = configuration.identifier as? IndexPath,
          let cell = collectionView.cellForItem(at: indexPath)
          else
    {
        return nil
    }
    
    let targetedPreview = UITargetedPreview(view: cell)
    targetedPreview.parameters.backgroundColor = .clear
    return targetedPreview
}

func collectionView(_ collectionView: UICollectionView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview?
{
    guard let indexPath = configuration.identifier as? IndexPath,
          let cell = collectionView.cellForItem(at: indexPath)
          else
    {
        return nil
    }
    
    let targetedPreview = UITargetedPreview(view: cell)
    targetedPreview.parameters.backgroundColor = .clear
    return targetedPreview
}

func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
{ 
 return UIContextMenuConfiguration(identifier: NSIndexPath(item: indexPath.item, section: indexPath.section), previewProvider: nil) { elements -> UIMenu? in
 ....

注意:我们将 NSIndexPath 作为标识符传递,因为它需要符合 NSCopying 协议。