How to achieve transparent background or rounded corners when dragging a UICollectionViewCell

1.3k views Asked by At

I'm sure there must be a simple way of doing this, but I've spent a long time down various rabbit holes without success so far.

I have a collection view which supports drag and drop. The cells being dragged have a UIImageView in the contentView, and the image view's backing layer has a corner radius applied. The background color for all views in the cell is clear.

When dragging, the cell has a white background which shows up around the corners of the image view:

white backgrounds when dragging cell

Is there a way of rounding the entire draggable view; or setting its background to clear so the annoying white border isn't visible?

UPDATE

It turns out the solution is embarrassingly simple (assuming UIBezierPaths fit your definition of simple):

You need to override the collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) method of the UICollectionViewDragDelegate protocol, and return UIDragPreviewParameters with the appropriate UIBezierPath set:

func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
    let previewParams = UIDragPreviewParameters()

    let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 140, height: 140), cornerRadius: 20)
    previewParams.visiblePath = path

    return previewParams
}

This is a naive implementation which hard-codes the CGRect from which the bezier path is derived - that works for my scenario because all cells are the same size. A more complex collection view would need some custom calculations here.

enter image description here

1

There are 1 answers

2
Mike Cole On

I think it's even easier than that. If your cell already has a clear background with a rounded UIImageView, you only need to set backgroundColor as .clear for UIDragPreviewParameters.

func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters? {
  let params = UIDragPreviewParameters()
  params.backgroundColor = .clear
  return params
}

Edit: as requested, here is a pared-down version of the cell

final class MyCell: UICollectionViewCell {
  // MARK: Constants

  private enum Constants {
    static let cornerRadius = CGFloat(8)
  }

  // MARK: Views

  private let imageView: UIImageView = {
    let imageView = UIImageView()
    imageView.contentMode = .scaleAspectFill
    imageView.layer.masksToBounds = true
    imageView.layer.cornerRadius = Constants.cornerRadius
    imageView.translatesAutoresizingMaskIntoConstraints = false
    return imageView
  }()

  // MARK: Lifecycle

  override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
  }

  required init?(coder: NSCoder) {
    super.init(coder: coder)
    setup()
  }

  override func prepareForReuse() {
    super.prepareForReuse()
    imageView.image = nil
  }

  // MARK: Public

  func update(with image: UIImage) {
    imageView.image = image
  }

  // MARK: Private

  private func setup() {
    contentView.backgroundColor = .clear

    contentView.addSubview(imageView)

    NSLayoutConstraint.activate([
      imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
      imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
      imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
      imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
    ])
  }
}