As far as I can tell, I need an UIView (or subclass of UIView) to display an CALayer on screen, right?
Then, what's the point of using CALayer for saving memory? The only point I see is when I would add several sublayers to a CALayer. Then those sublayers would not get copied 3 times for all the different tree types like presentation tree, render tree and so on. Is that right?
You can add CALayers as sublayers of another CALayer. Each individual layer does not have to be backed by a view, but the root layer in the hierarchy must be backed by a UIView.
The point is not to avoid having copies of the CALayers, which are quite lightweight, but to avoid having copies of UIViews or, more specifically, the graphics contexts that back UIViews. Those take up considerably more memory.
Related
What are the differences between adding a view as a subView VS drawing the view in that view's drawRect method?.
I use both approaches, but I was wondering apart from the obvious such as, if you do in drawRect it will be a single View, as opposed as two views, or adding as a subview is just easier. Are there any specific situations where you should definitely use one over the other?
Overriding -drawRect: can be a good way to draw complex UITableViewCells. Having all of the subviews composited and drawn as one view helps table view scrolling performance immeasurably.
Having said that, I typically stick with the highest level API available and drop down to a lower level only if performance suffers.
Adding a subview is easier as you point out, and I really see this as no contest in 90% of cases. You should generally add a subview and let the libraries handle drawing of subviews in their correct position.
I only use -drawRect: to accomplish custom drawing within my view, but not to draw subviews, it creates unnecessary complexity. If you need a lot of performance, -drawRect: can help you there. Also, in the case of simple drawing, -drawRect: is very nice as opposed to making several subviews. But in general, it pays to just add a subview.
If in the future you decide you want these subviews to receive touch events or handle things interactively, it is more difficult to refactor your -drawRect: code.
I'm in the process of trying to tweak the process of a game which uses Quartz to draw a moderate number of sprites, around 50, during each game loop (40 fps).
At the moment I have a very simple drawing mechanism where a single game view (UIView) iterates over all the active sprites and asks each one to render itself to a CGContext (that of the view). This works fine, but performance is starting to drop with more than 50 active objects and I'd quite like to tweak things.
I've decided that holding a CALayer for each sprite which its cached drawing is the way to go - then using Core Animation to render/rotate/scale the drawing.
I'm struggling to understand how I achieve this. Where exactly do I initially draw to? I don't have a CGContext when my sprite is initialised. Is the right approach to render to a CGImage as a buffer then set it as the content on the CALayer?
Just subclass CALayer and do your drawing in drawInContext:. The layer will then cache its contents automatically. If you don't want to subclass CALayer, you can also assign it a delegate (which must not be a UIView) and implement drawLayer:inContext:.
I would like to implement a drawing class with the help of Quartz.
I want to be able to save parts of what is drawn on separate layers. I want these layers to be retrievable, so I can delete/hide/show layers on command.
Can I save multiple CGLayerRef as a NSMutableArray property of my class and then be able to retrieve them? If yes, can you point me to an example.
If there are any flaws in the "architecture" above, please point me to alternative solutions that could help me accomplish layered control over graphs.
Thank you.
CALayers that you create, can of course be stored in NSMutableArray, and you can work with them later on, usually by animating their properties, or asking them to redraw themselves.
Usually you create a custom UIView, create and manage layers within that view. Those layers are either member variables of that view, or you store them in an array. As things are happening in your app, your view animates the layers accordingly. Usually you want to react on touch events (which you also implement in that particular view - touchesBegan/Moved...) and animate the layers.
CALayer draws itself and caches the content for as long as you call [layer setNeedsDisplay], or it's bounds (size) are changed (well, if needsDisplayOnBoundsChange is true). Practically in all my apps I did, such redrawing happens very rarely - only if data are changed, and layer needs to redraw. Animating layers, transforming their size, rotation, changing position - layer is not redrawn during any of these. Hiding, showing, changing transparency - no redraw is required.
That "drawing class" you are talking about - you actually have only two options - either you extend CALayer and overwrite drawInContext:, or you create basic CALayer, set its delegate, and there you draw in drawLayer:inContext:. I personally prefer creating delegates.
I think that adding a CALayer as "subview", somehow, does save a lot of memory. A UIView always comes with 3 copies of it's content bitmap (presentation layer, render tree and another one, plus the view itself, so every pixel is saved 4 times). But how could that be done?
UIView is basically a wrapper for CALayer. So you can add a layer directly to the view's layer. This can be done by calling
[[theView layer] addSublayer:newLayer];
I'll start by saying what I want to do because I'm unsure if I'm asking the right question. I'm making a grid based map and am going to hold an array of objects to keep the state and presentation of the map. Each object will be of a Tile class. Should I be subclassing UIView or sublass NSObject and have an ivar of UIView. I was also planning to have a UIImageView inside the UIView to load the image that represents that bit of the map. Lastly, I wanted to load the view from a NIB.
Individually I know how to do each of these things but unsure of the best practice. Any thoughts?
Have you considered Core Animation layers? You can create a single view with a grid of layers each with their own bounds and position within the view. It sounds like it might give you what you need. On the phone, setting a layer's contents with an image is as simple as this:
CALayer *gridLayer = [CALayer layer];
[gridLayer setContents:(id)[gridImage CGImage]];
[gridLayer setBounds:[gridImage bounds]];
// Position the layer's center a x:25.0 y:25.0 within the view
[gridLayer setPosition:CGPointMake(25.0f, 25.0f)];
[[view layer] addSublayer:gridLayer];
The variable gridImage is a UIImage* you've allocated somewhere. You would just need to calculate your layer rects and positions and place them accordingly.
Make sure you have a clear definition of which particular classes/objects comprise your model, your controller and your view. Your data and data logic should be in the model, your display in the view and you should tie them together with the controller.
The simplest way to accomplish a grid is to use UIImageViews that are positioned by a single controller. The model will track the logical relationships between the map squares and relay that to the controller which will load the images into the UIIMageView. The controller will handle loading everything from nib.
However, using CALayers is the preferred cutting edge method. The basic model,controller, view relationship is the same except that the CALayers replace the ImageViews.
If I uderstand right, you can make subclass of UIImageView.