macoscocoauicollectionviewdrag-and-dropnscollectionview

Additional view in NSCollectionViewItem pauses dragEvent in CollectionViewController


I am trying to implement drop delegates on a NSCollectionViewController and having issues using a custom NSCollectionViewItem with an additional View Layer I've added onto the CollectionView Item. FWIW, The additional view is used draw a dashed border to indicate a drop area.

The drag event works fine on this collectionItem, and all other collectionItems without this view when it is hidden, but as soon as the drag event occurs on top of this view, the drag event pauses.

The drag event resumes as soon as the mouse is dragged outside of the view, but nothing happens if I release the drag while the mouse is over the view.

I would love to know what is happening here and how to prevent the custom view from "stealing" the mouse event from the CollectionViewContoller.

Delegate Method on DropViewController

    func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
        print("1")


        if proposedDropIndexPath.pointee.item <= self.destinationDirectoryArray.count {
            if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
                return .move
            }
        } else if proposedDropIndexPath.pointee.item == self.destinationDirectoryArray.count {
            //There's some stuff here validating the URL removed for brevity. It works okay when the focus is outside the view, but happy to add back in if helpful

            if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
                return .move
            }
        }
        return[]
    }

Configuring Collection View

func configureCollectionView() {
    let flowLayout = NSCollectionViewFlowLayout()
    flowLayout.minimumInteritemSpacing = 8.0
    flowLayout.minimumLineSpacing = 8.0

    destinationCollectionView.delegate = self
    destinationCollectionView.dataSource = self
    destinationCollectionView.register(NSNib(nibNamed: "DestinationCollectionItem", bundle: nil), forItemWithIdentifier: directoryItemIdentifier)
    destinationCollectionView.collectionViewLayout = flowLayout
    destinationCollectionView.registerForDraggedTypes([.fileURL])
    destinationCollectionView.setDraggingSourceOperationMask(NSDragOperation.move, forLocal: true)
}

Collection View Item Setup

    class DestinationCollectionItem: NSCollectionViewItem {

        @IBOutlet weak var backgroundLayer: NSView!


        override func viewDidLoad() {
            super.viewDidLoad()
            self.highlightState = .none
            view.wantsLayer = true
            view.layer?.cornerRadius = 8.0
            backgroundLayer.isHidden = true
        } 

}

Custom Border View - Applied custom class in Xib and linked to File's Owner

class BorderedView: NSView {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        let path : NSBezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 10.0, yRadius: 10.0)
        path.addClip()

        let dashHeight: CGFloat = 2
        let dashLength: CGFloat = 7
        let dashColor: NSColor = .lightGray

        // setup the context
        let currentContext = NSGraphicsContext.current!.cgContext
        currentContext.setLineWidth(dashHeight)
        currentContext.setLineDash(phase: 0, lengths: [dashLength])
        currentContext.setStrokeColor(dashColor.cgColor)

        // draw the dashed path
        let cgPath : CGPath = CGPath(roundedRect: NSRectToCGRect(self.bounds), cornerWidth: 10.0, cornerHeight: 10.0, transform: nil)
        currentContext.addPath(cgPath)
        currentContext.strokePath()
    }

}

Solution

  • Well - I solved this one pretty quick.

    While I previously tried adding unregisterDraggedTypes() to the backgroundLayer, the issue turned out to also be occurring on the image layer. I applied it to both the Image and backgroundLayer and it works now.

    Collection View Item Setup

    class DestinationCollectionItem: NSCollectionViewItem {
    
        @IBOutlet weak var backgroundLayer: NSView!
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.highlightState = .none
            view.wantsLayer = true
            view.layer?.cornerRadius = 8.0
            backgroundLayer.isHidden = true
            backgroundLayer.unregisterDraggedTypes()
            self.imageView?.unregisterDraggedTypes()
            self.textField?.unregisterDraggedTypes()
        }
    
    }