Search code examples
xcodemacoscocoacore-animation

Annotating over an NSImageView with CALayers, layer-hosting or layer-backed?


I would like to an annotate over an NSImageView with CALayer. The purpose is to be able to draw a shape (a CALayer) over the image, and in a separate view display information about the area that has been highlighted. For example, the user could draw a rectangle over the image view and the pixels under this area could be magnified and displayed in another window.

I would like some general advice on the best way to implement drawing over the top of an image view using CALayer. If the view is resized I want the position of the selection area to stay synchronised, that is, the position relative to the image should remain constant.

I'm slightly confused because the CALayer documentation states for layer-backed views,

  • "When using layer-backed views you should never interact directly with the layer. Instead you must use the standard view programming practices."

    Clearly this is not the appropriate choice because I want to own the layer and interact with it directly.

For layer-hosting views,

  • "When using a layer-hosting view you should not rely on the view for drawing, nor should you add subviews to the layer-hosting view."

    So it would seem the view should be layer-hosting. However, making my NSImageView (subclass) layer-hosted prevents the image from drawing because NSImageView relies (I assume) on -drawRect:. -drawRect:is not called with layer-hosting views. It also wish to do some custom drawing in the view when it is empty e.g. a placeholder message, for that I need -drawRect:.

So one possible solution would be in Interface Builder place an "annotation view" above the image view, i.e. they are not nested but simply occupying the same space and have the same size. I can specify the z ordering in Interface Builder so the annotation view is always on top. I then make the annotation view be layer-hosting, and leave the image view unchanged.

This seems like a hack, so I'm sure there is a better way. Of course I could draw the annotations in the image views -drawRect: but this would add lots of code and I dont want to do that right away without exploring alternatives.


Solution

  • The solution I went for was to not alter the NSImageView subclass's layer at all. Instead I added a subview to the NSImageView subclass, constrained the view such that is has the same bounds as the parent, and then made this subview layer-hosting. It is then possible to add sublayer to this 'annotation' view's layer. In this way I can annotate on top of an NSImageView.