Search code examples
iosdrag-and-dropuikittransparency

UIDragInteractionDelegate: How to display transparent parts in the drag preview returned by dragInteraction(_:previewForLifting:session:)


I'm building a drag and drop interaction for an iOS app. I want to enable the user to drag and drop images containing transparent parts.

However, the default preview for the dragged contents is a rectangle with an opaque white background that covers my app's background.

When I create a custom preview by implementing the UIDragInteractionDelegate method dragInteraction(_:previewForLifting:session:), as in Apple's code sample Adopting Drag and Drop in a Custom View, the transparency of my source image is still not taken into account, meaning my preview image is still displayed in a rectangle with an opaque white background:

func dragInteraction(_ interaction: UIDragInteraction, previewForLifting item: UIDragItem, session: UIDragSession) -> UITargetedDragPreview? {
    guard let image = item.localObject as? UIImage else { return nil }

    // Scale the preview image view frame to the image's size.
    let frame: CGRect
    if image.size.width > image.size.height {
        let multiplier = imageView.frame.width / image.size.width
        frame = CGRect(x: 0, y: 0, width: imageView.frame.width, height: image.size.height * multiplier)
    } else {
        let multiplier = imageView.frame.height / image.size.height
        frame = CGRect(x: 0, y: 0, width: image.size.width * multiplier, height: imageView.frame.height)
    }

    // Create a new view to display the image as a drag preview.
    let previewImageView = UIImageView(image: image)
    previewImageView.contentMode = .scaleAspectFit
    previewImageView.frame = frame

    /*
         Provide a custom targeted drag preview that lifts from the center
         of imageView. The center is calculated because it needs to be in
         the coordinate system of imageView. Using imageView.center returns
         a point that is in the coordinate system of imageView's superview,
         which is not what is needed here.
     */
    let center = CGPoint(x: imageView.bounds.midX, y: imageView.bounds.midY)
    let target = UIDragPreviewTarget(container: imageView, center: center)
    return UITargetedDragPreview(view: previewImageView, parameters: UIDragPreviewParameters(), target: target)
}

I tried to force the preview not to be opaque, but it did not help:

previewImageView.isOpaque = false

How can I get transparent parts in the lift preview?


Solution

  • Overwrite the backgroundColor in the UIDragPreviewParameters, as it defines the color for the background of a drag item preview.

    Set it to UIColor.clear which is a color object whose grayscale and alpha values are both 0.0.

    let previewParameters = UIDragPreviewParameters()
    previewParameters.backgroundColor = UIColor.clear // transparent background
    return UITargetedDragPreview(view: previewImageView,
                                 parameters: previewParameters,
                                 target: target)