0
votes

According to the documentation for NSView's drawRect:

If your app manages content using its layer object instead, use the updateLayer method to update your layer instead of overriding this method.

I have an NSView with subviews that are provided by the framework, and they all draw using drawRect:. This framework-provided view is a subview of an NSView for which I require a layer. Because my framework-provided view is a descendant of a layer-backed view, drawRect: isn't usually called, especially in cases where the window is made active or inactive (the view needs to update to reflect its (in)active state).

Of course if I make my containing view not layer backed, updates occur when the window is made active or inactive.

Without modifying the framework into a custom fork, what's the best avenue for making sure drawRect: occurs when needed in my framework-provided view?

Thanks.

Edit 25-Aug-2018:

It looks like the trick is to set one of the views in the hierarchy to, e.g., [view setCanDrawSubviewsIntoLayer:YES, which according to the documentation uses all of the subviews’ drawRect: to add their drawing to its own layer. However this seems to work only through 10.13, and is broken in the 10.14 beta. I'll continue to look for a potential API change, unless this is a 10.14 beta bug.

Since the issue is still unresolved, it's not really answered yet.

1
I think you might be confused. I'm not sure exactly what you're asking, but the -drawRect: method of a superview is not responsible for calling/triggering/invoking the -drawRect: of its subviews. Each view (layer or not) gets drawn when it changes, and the magic display manager in Cocoa composites the resulting images. Said another way, the needsDisplay state (which eventually cause a new -drawRect: message) is independent of its superview and subviews.James Bucanek
I'm not suggesting that drawRect: is responsible for subviews. I'm saying that if I have a view B which is drawn via drawRect: on a layer-backed view A, then my view B doesn't have its drawRect: called when I focus or unfocus the window. Descendants of layer-backed views become layer-backed, too, thus the documentation quoted indicates drawRect: will not be called. If I don't add a layer to view A, then my view B drawRect: is called when needed.balthisar
Oh, I think I get it now. So why can't your custom NSView simply observe its window's NSWindowDidBecomeMainNotification and 'NSWindowDidResignMainNotification` notifications and send a needDisplay=YES message to all of its subviews? Seems simple (and harmless) enough.James Bucanek
I don't have a custom NSView, unless I want to fork the framework. If I can find a generic solution, then I think a PR would be welcomed, but mostly I'm trying to find the cause and solution for the difference in behavior. There's a difference between hacking it to work, and figuring out what the difference is and doing it the right way. I'm not a paid pro, so I'd prefer to understand what's happening rather than just brute force it.balthisar
Just to be clear, you don't have to subclass the NSView. Any object can observer a notification and take some action (your window controller, for example). If you think the framework controls are misbehaving when inside a layer-backed view, then definitely file a bug report. But I suspect there's a hack involved...James Bucanek

1 Answers

1
votes

Layer-backed views which don't override -wantsUpdateLayer to return true still draw themselves using -drawRect:. The bit of documentation you quoted is using "should" to mean "should, for best performance,". It's not required, it's just recommended.

Views don't generally redraw themselves just because the containing window has changed key or main status. You would have to mark them as needing display. Or the framework should be doing that.

I suspect the reason that it works when your view is not layer-backed is that you are marking your view as needing update. Since non-layer-backed views draw into the window's backing store using the painter model (back to front), if your view redraws itself then any subviews will have to redraw themselves on top of your view's drawing.

If the framework's views need to redraw when the window's key/main status changes, then they should be observing the relevant notifications and setting themselves as needing display. If they're not doing that, it's a framework bug. You can work around it by marking them as needing display yourself.