Ram Patra
Ram Patra

Reputation: 16664

How to detect which CAShapeLayer was clicked on in macOS using Swift?

I have a view with many CAShapeLayer objects (there can be other CALayer objects as well) and I want to modify the CAShapeLayer object that the user clicks on.

I was experimenting with the below two methods but none of them works. Any tips would be great.

Approach one:

private func modifyDrawing(at point: NSPoint) {     
   for layer in view.layer!.sublayers! {
       let s = layer.hitTest(point)
       if s != nil && s is CAShapeLayer {
           selectedShape = s as? CAShapeLayer
       }
   }
   // modify some properties
   selectedShape?.shadowRadius = 20
   selectedShape?.shadowOpacity = 1
   selectedShape?.shadowColor = CGColor.black
}

Approach two:

private func modifyDrawing(at point: NSPoint) {
    let drawingsAtMouseClick: [CAShapeLayer] = view.layer!.sublayers!.compactMap{ $0 as? CAShapeLayer }

    if drawingsAtMouseClick.isEmpty {        
        return
    }

    for drawing in drawingsAtMouseClick {
        if drawing.contains(point) {
            selectedShape = drawing
            break
        }
    }
    // modify some properties
    selectedShape?.shadowRadius = 20
    selectedShape?.shadowOpacity = 1
    selectedShape?.shadowColor = CGColor.black
}

The point parameter passed to these functions is the NSEvent.locationInWindow. Not sure whether I should convert this with respect to the CAShapeLayer or something.

P.S.: This isn't production code so please kindly ignore Swift best practices, etc.

Upvotes: 0

Views: 32

Answers (1)

Duncan C
Duncan C

Reputation: 131418

The CALayer.hitTest(_:) method will tell you the layer that was hit, including in a layer's sublayers.

You shouldn't need to check every sublayer. You should be able to ask your view's layer what layer was hit by asking the top-level layer.

A view's layer's coordinates are generally the same as the view's bounds. It's anchored at 0,0 in the parent layer, and the sublayers use that coordinate space. Thus, you should convert your point to view/layer coordinates before hit testing.

(I always have to go check to see which coordinate systems are flipped from the other between iOS and Mac OS and views and layers. You might need to flip coordinates. I leave that research up to you.)


Edit:

I seem to remember that CALayer.hitTest(_:) just checks that the layer's frame contains the point, not that it actually contains an opaque pixel at that position. It's more complex if you want to check to see if the point contains an opaque pixel.

Upvotes: 1

Related Questions