iPhone - Using CALayers instead of UIViews - iphone

I've always been told that if you want to display things into a view, you must add subviews into it and then draw into those subviews.
And today, I see that question (searching for something else) : IPhone SDK: Camera access?
In the accepted answer, I see that its author uses CALayers to draw 4 sub "views" into the main view, but all of this only based on CALayers...
So I wonder... Why ? Is this a correct way of doing ? What could lead me to use seblayers instead of subviews ?

I've been using both UIViews and CALayers. It really depends on what you want and need. UIView uses CALayer in the background, so you're using CALayers in either case. Note that there's a layer property in UIView of the type CALayer. However, coding directly with UIView is easier but it doesn't expose the full powers of Core Animation. Coding with CALayer does expose you to Core Animation and a dozen of other APIs.
If you're just concerned adding subviews, either approach would work. You can think UIView hides some of the CALayer implementation details.

Related

UIScrollView lags on iPhone

I'm trying to create a crossword app. The game runs in a UIScrollView, because the player should be able to zoom, scroll etc. The largest crossword are 21x21 which means there need to be 441 touchable tiles. What i've tried so far were to create an UIView and added it as a subView to the UIScrollView. Then I call a method that creates 441 custom UIButtons and set the backgroundImage.
Some of the UIButtons need to have a custom label overlay, so i added a UILabel and set it on top of the UIButton.
When I run the app in simulator all works as it should, but when i test it on an iPhone 4 the UIScrollView lags a lot.
I don't know if this the way to do it? Can you maybe try to guide me in the right direction of how to do this so the UIScrollView won't lag on a device.
Thanks in advance.
Big UIScrollViews are difficult to make smooth without some sort of caching mechanism, such as the one provided by UITableView.
One thing you can do, however, to make life easier for your rendering, is to make sure to use opaque backgrounds whenever possible. In stead of making a view transparent you should make it the same color as its underlying view. This is something that does help.
Also, I have experienced better performance using imageWithContentsOfFile: in stead of imageNamed: for initializing images to be used in a UIScrollView.
In order to not keep all those buttons in memory, you should work with reusable table view cells, this will really improve the scroll performance. Here's one component that could make your life easier: DTGridView, there are also others (you can check Cocoa Controls)

iPhone - UIView adding a subView vs drawRect?

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.

UIScrollView performance problem because of lots of labels

I've got a pretty big UIScrollView (contentSize 8000 x 960) with a lot of small labels.
The labels are only added if you scroll to the area where the label belongs to. So, at first, the scrooling is smooth, but as more and more labels are loaded, the performance suffers more and more.
What's the best solution to keep the scrolling smooth? Is CATiledLayer the way to go? Or should the labels off the view should be hidden or something like that?
Thanks a lot for your help!
Stefan
EDIT:
I got a huge performance boost when drawing some objects instead of using views; but now, I've got another problem; if I draw directly onto the UIScrollView, everything performs great. But if I lay a UIView on the UIScrollView and draw on that, performance goes down again (both times calling setNeedsDisplayInRect: in scrollViewDidScroll:).
So hierarchy:
UIWindow
UIView
UIScrollView <== drawing here
works fine.
But hierarchy:
UIWindow
UIView
UIScrollView (size 8000x960)
UIView (size 8000 x 960) <== drawing here
performs bad.
Any ideas why? I need the UIView above because the drawing should be ABOVE some UIViews; so I place the UIViews in the scrollView and draw in the view above...
The label reuse is one of possible solutions. Sometimes it doesn't help a lot. When a new UIView appears the drawRect method will be called and that may cause problems with animation.
Sometimes the best solution is to draw your labels (I mean text) directly using Core Graphics without creating UILabel object or something else. The CALayer will increase performance, but there is a possibility that it will not help you a lot.
But first of all you should remember that views with alpha = 0 or hidden = YES will not be drawn at all, so there is no penalty for using them. Maybe you should try to hide unused ones before using Core Graphics.
The best way would be to have just a few UILabels and reuse them. But you'll have to implement - (void)scrollViewDidScroll:(UIScrollView *)scrollView and do some reusable logic there. Depending on case it might be easier to use a UITableView and transform it 90 degrees, so it looks like a scrollview. That way you can the build-in reusable logic of the TableView.
For others seeing this post, I was able to drastically improve performance and keep UIViews in a scroller without using a table by enabling each view's "shouldRasterize" property. This property renders the view as an image while the view is in motion, causing it to not need to be reprocess for each pixel it moves. So, if I have a UIButton called button, it gets enabled like this:
button.layer.shouldRasterize = YES;
Did u try something similar to UITableViewCell
dequeueReusableCellWithIdentifier
Using a reuseIdentifer to reuse the once allocated labels..

Drawing class to allow layer control

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.

How can I blend between my UIView and my EAGLView?

I have an opengl scene rendering on an EAGLView layer and some other elements (circles and such) rendering on a UIView (which is a sibling of the EAGLView, positioned above it). Is it possible to blend colors between the two layers? I'd like to do some difference blending to get an inversion effect on the colors from EAGLView.
I've been playing around with CGBlendMode but it only seems to affect what I'm drawing in that current view. I think this has something to do with the CGContext but I'm a little hazy on the details, can I force the UIView and the EAGLView to have the same CGContext so that the blending works between them?
Help, corrections, clarifications are all appreciated. Thanks in advance,
-S
Short answer is you can not. Long answer follows.
By EAGLView you must mean the subclass of UIView that is included in the OpenGL ES Template in Xcode. What makes this class special is that the layerClass class method is overridden and return the CAEAGLLayer class instead of the CALayer class, as is default.
UIView and CALayer work in pairs. All UIView objects are backed by a CALayer, the CALayer is the object responsible for layout and rendering to screen. The UIView is a delegate to the CALayer, and is responsible for drawing it's graphics when needed.
CALayer will let it's delegate (the UIView) draw into a CGContextRef. It is one context per UIView, so you can not use CGBlendMode to blend several views since it will only function within one single UIView context.
Blending of CALayer should be done using the filter properties. These are defined for iPhone OS but the available filters are undefined according to the documentation. This is because Core Image is not available on iPhone OS at this time.
I don't think you'll be able to blend colours in that sense. The best you can do is have one completely obscuring the other, or have the top layer semi-transparent (in which case, you'll see the part underneath) - but you won't be able to do XOR type drawing.