Как скрыть черный фон UIContextMenuInteraction/UITargetedPreview?

Я хочу добавить UIContextMenuInteraction в UIView с некоторыми прозрачными частями. Когда пользователь взаимодействует с этим представлением, iOS показывает это представление на черном фоне.

https://i.s tack.imgur.com/ZSL8D.jpg

Можно ли сделать эти части прозрачными или изменить цвет?

3 ответа

Решение

Наконец я нашел решение! Вам необходимо реализовать эту функцию, если вы хотите показать пользовательскую кривую:

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)
}

Как сказал MAGiGO , вы можете использовать previewForHighlightingContextMenuWithConfiguration и previewForDismissingContextMenuWithConfiguration делегировать методы, чтобы создать свой собственный 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 протокол.

Другие вопросы по тегам