When to use NSView's canDrawSubviewsIntoLayer (layer squashing)? - core-animation

The benefits of setting canDrawSubviewsIntoLayer = true seem to mainly relate to a reduction in memory usage (reference: Apple 2013 WWDC talk 215).
I would think that this would be mainly beneficial for subviews that are more "static" in nature as opposed to subviews whose frames are effectively moving around a lot. I am trying to get clarity on this.
In particular: if you have a grid-like parent view (like NSTableView or NSCollectionView) where each of the child cells (custom NSView instances) is being moved around a lot (queued/dequeued/reused) to allow for scrolling, would it be a good idea to have these cells drawn into the parent layer OR should they be treated as their own individual layers?
[Just to be clear, the main consideration is scrolling performance.]

Related

NSCell vs NSView: when many controls are needed

I am aware that Apple is deprecating the use of NSCell in favour of NSView (see AppKit 10.10 release notes). It was previously recommended that NSCell be used for performance reasons when many controls were needed.
I have spent considerable time implementing a custom control that required many subViews and the performance using NSView-type subViews was not good. See related stackoverflow discussion What are the practical limits in terms of number of NSView-type instances you can have in a window? I was struggling with 1000-2000 in-memory objects (which doesn't seem a lot). What is the actual reason for this limitation?
One thing that confuses me on the above is View-based Cocoa NSTableViews. You can create tableViews with more than 1000-2000 cells and they don't seem to have poor loading and scrolling performance? If each of the cells is an NSView then how is this achieved?
If there are practical limits, then what are Apple thinking when they say they are deprecating usage of NSCell's? I am sure they are aware that some controls need a large number of subViews.
Further, an (probably outdated) Apple Developer Guide give the following explanation for the difference between NSView & NSCell which I need explained further:
"Because cells are lighter-weight than controls, in terms of inherited data and behavior, it is more efficient to use a multi-cell control rather than multiple controls."
Inherited data: this would surely only cause "bloat" if the data was being used => and it would only be used it you needed it?
Inherited behavior: methods that you don't use in a class/object surely can't cause any overhead ?
What is the real difference between the lightweight NSCell versus the heavyweight NSView other than that it just seems to be conventionally accepted?
(I would really like to know.)
A brief and incomplete answer:
NSCells are about drawing state, and not much else. NSViews must draw, but also must maintain and update all kinds of other information, such as their layout, responding to user input events, etc.
Consider the amount of calculation that must happen if you resize a view that contains many hundreds of subviews: every single subview position and size must be maintained according to the existing constraints for the view layout. That alone is quickly a very large amount of processing.
NSCells, in contrast, don't exist in a layout in that way. Their only job is to draw information in a given rectangle whenever asked to do so.
One thing that confuses me on the above is View-based Cocoa NSTableViews. You can create tableViews with more than 1000-2000 cells and they don't seem to have poor loading and scrolling performance? If each of the cells is an NSView then how is this achieved?
NSTableViews re-use views. The only views actually generated are those that are visible, plus maybe a row of views above and a row below the visible area. When scrolling the table, the object value associated with a given row of views is changed to that the views in the row will display different content

Is it better to layout subviews in layout/layoutSubviews method?

This question is for both cocoa and cocoa touch. But I'll write an example just for cocoa.
As I understood, I can setNeedsLayout to YES multiple times in a cycle and -layout will be called just once. But are there any other benefits of laying out subviews in -layout method?
Explanation / example: At the moment I'm laying out my subviews in custom viewController (that has default NSView) every time I call custom redraw method. And I call redraw method only when user changes some properties so I really want to relayout subviews.
There are plenty of external circumstances not under your direct control that might cause the system to want to lay out your views. For example, device rotation or incoming calls on iOS, or window resizing on OS X. If you have your layout logic in the standard places, then your code accommodates these without any additional effort, and in the places your internal state changes, you can request such a layout explicitly.
To turn your question around: is there a significant benefit to not doing your layout in the standard way? Do you believe that this will be a performance issue? Have you measured it to see whether it is actually a performance issue?

Best practices when using drawRect

I have recently started creating my own controls and I seem to have a bit of trouble understanding how I should use drawRect.
Basically I have 3 Questions.
Is it a good idea to have conditional drawRect's? ie. different drawing code based on properties or instance variables.
What is the best method for animating changes to the drawRect's drawing? For example, a fuel gauge control with animated fill and un-fill.
And, finally, the examples I have seen for animating with drawRect tend to use timers, is that really a good method in practice? It seems like the heavier apps would have issues with that method.
I guess a 4th would be, is there, perhaps, a better place to do this kind of stuff?
Is it a good idea to have conditional drawRect's? ie. different drawing code based on properties or instance variables.
Sure, why not? If your drawRect: method becomes unwieldy, you could split it into multiple methods that you then call from drawRect: depending on the properties of your view. E.g. you could have methods like drawBackground, drawTitle, etc.
What is the best method for animating changes to the drawRect's drawing? For example, a fuel gauge control with animated fill and un-fill.
That depends. For very small views, you could call setNeedsDisplay from a timer, but for larger views, you'll often run into performance issues with this approach.
Animating changes is often better done by compositing your view out of multiple subviews or layers that can be animated with Core Animation (or the simplified UIView animation methods).

Using Opacity-generated layers in Quartz 2d instead of drawing to the view's layer

Firstly, be gently with me. I'm new to Objective C and iOS programming.
I've just purchased a copy of Opacity and I'm playing around with layers. My only grade A success so far is to design and successfully generate my own design of a calculator - so I haven't got that far!
Specifically, I want to know how to use Opacity CALayer subclasses to 'replace' the backing layer on a UIView. Adding sublayers to the existing view layer is obvious enough - and I've managed to get that working - but how do I use an Opacity CALayer subclass to provide the initial content of the view's backing layer? I've tried copying the drawInContext code from an Opacity subclass into drawRect on my UIView subclass that hosts the proxy view for the controllers' 'view' property but (for some reason I can't fathom) that didn't work.
I assume that the view's original backing layer is item '0' in the layer array? My ultimate goal is to have just my layers behind the view without having to 'ignore' the original one.
If there's a chance that Dr Brad Larson might read this;...
I've watched your Quartz 2D and animation videos over and over and I've read all of the copies of your course notes that I can lay hands on but all the examples I've found start with an image already in the hosting view - onto which more layers are added, which isn't really what I"m after. I've also read the Big Nerd Ranch Guide and got all of Conway and Hillegass's example code too but - I'll be darned - they also start with an image already in the view!
If any one can help me out - just point me at the relevant documentation, please don't bother writing huge tranches of code here - I'd be seriously grateful.
VV.
PS: I'm deliberately NOT using IB yet as I want to grip the underlying mechanics of Cocoa Touch first and I won't use dot notation on principal :-). (See my profile!).
The trick is to override the view's class method 'layerClass' to be the base layer sub-class that's needed before nailing other layers into the hierarchy. This builds the view's base (implicit) layer when the view is instantiated and then I can slap my other layers down afterwards.
This is a fun game. I'll get the mind-set soon!
This technique differs markedly from the same requirement using NSViews which have a 'setLayer' instance method that can be used to change the implicit layer AFTER instantiation. An expensive procedure not offered on a lightweight object like a UIView.

Subview of NSView or CALayer?

Today I decided to try to use CALayers to show a rectangular box overlaying a NSView. The layer will contain some text and will be turned on and off depending on when it is necessary to show the variable text. The reason I wanted to use CALayer for this was the nifty rendering and animation you can easily do with CALayer. I implemented my layer and it worked like a charm. However, after using my GUI and clicking several times on various buttons turning the layer on and off, it seemed that the hierarchy of what I thought was my layer view was skewed. I think focus must have been switched to some other NSView which again was turned off. I basically got very confused as to which layer I was handling at a given time and I lost control of the view hierarchy.
My question is: should I use subviews of NSView, or CALayers to show something that may occur many times on and off in an application? It seems to me that it is easy to loose control of which layer you are working on. Is there a way to identify by name the current layer so you can reuse the layer, or is it best to work with layers, delete them and then re-create the layer the next time you need them?
Thanks for your time. Cheers, Trond
FWIW,
I have often turned CALayers "on and off" very quickly, with no problems at all. So you can do that if you want to!
Deleting them and recreating them quickly on the fly, does not seem to cause any problems. (It's not a big resource user, and I've never seen any other problems doing that.) So definitely do that if you want to!
I actually don't understand what you mean about "naming" layers - of course, as iVars they have a name! You have to name all your CALayers.
Here's a typical bit of production code (from a .h file):
CALayer *rearLayer;
CALayer *hugeBasket; // holds everything for ez-on/off
CALayer *theActualSkyline; // nb, same name as similar UIView
CALayer *someTrees; // minor stuff
CALayer *someBushes; // overs
// for the stupid help basket..
CALayer *LLDRear;
CALayer *LLDArrowLeft;
CALayer *LLDArrowRight;
CALayer *LLDPointlessUpArrow;
CALayer *LLDYetAnotherStupidShadow;
// etc etc..
And so on and on. I don't really see how you can "not" name them, you know!
Finally,
4, Don't forget layers are much "better" than NSViews, because: NSViews have shoddy/buggy relationship between overlapping siblings: they essentially don't work. Read about that here:
port an iOS (iPhone) app to mac?
Hope it helps!
PS - these may also help with CALayers...
Exactly what should happen in a CALayer's display/drawRect methods?
What's the difference and compatibility of CGLayer and CALayer?