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.
Related
When you add a subView to UIScrollView, should this change the smoothness, or anything else for the UIScrollView? For example, if you have a UIScrollView with mainly text, then add a couple of views on top of it in order to add an effect to the text, should this change any of the gestures, or smoothness of the original UIScrollView without the subViews? Thanks.
It won't change the gestures.
As for the smoothness, it depends on how heavy your subviews are. I guess that if you add a lot of them, and they require to render a lot of dynamic content and such, it could make the scrolling a little rough, but if you are adding only a few subviews you should be fine.
As a comparison, think that a UITableView is a UIScrollView with a LOT of subviews on it (each cell is a subview, and cells have several subviews too), and it usually renders fast.
If the sub views you implement have the worst drawRect: implementation in the world, you don't need very many views to get slow, un-smooth scrolling.
You should profile your code in Instruments with the Time Profiler, and see what causes the slow scrolling.
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..
I have a UIScrollView with a custom content view that uses drawRect to display its contents.
In my drawRect method I respect the passed CGRect parameter and only draw what's needed.
However, each time the drawRect is called it is passed the entire bounds of the view, even if it is several thousand pixels, and I'm worried about performance and memory use. Is there any way to make a UIScrollView limit this, or am I worrying too much over nothing?
Also, I've tried using CATiledLayer as the layer, and while this corrects the passed rect, it feels like I'm misusing the class. Also, the view still keeps all the pixels backed as far as I can tell. (Even if it doesn't draw some of them right away)
using CATiledLayer is probably the best option. I'm familiar with CATiledLayer as a concept, but never used it myself, so I'm not going to say anything about that. What I can say, is that if you subclass the UIScrollView and implement
layoutSubviews
you should be able to redraw the subviews toll-free with good performance, as long as you implement a construction using
CGRectIntersectsRect()
In which you can see if the current visible rect, in the case of a subclass
[self bounds]
intersects with the rect of the object you want to draw. if it doesn't, you can choose to ignore the object or even (if it is already on the scrollview) remove it from superview. if it does, you can place the object on superview.
again, CATiledLayer is a way better solution, but if you feel like you are misusing it, try the approach I described.
1.content view large than scrollview is true..
2.may be you should realize UiScrollViewDelegate's
scrollViewDidScroll
caculate content view 's rect that should be redrawed ,not the entire bounds .
I'm contemplating subclassing UIScrollView (the way UITextView does) to draw a fairly large amount of text (formatted in ways that NSTextView can't).
So far the view won't actually scroll. I'm setting contentSize, and when I drag, I see the scroll indicator. But nothing changes (and I don't get a drawRect: message).
An alternate approach is to use a child view, and I've done this. The view can be over 5000 pixels high, however, and I'm a bit concerned about performance on an actual device.
(The other approach, be like UITableView, would be a huge pain -- I'm "porting" Mac Cocoa code, and a collection of views would be a huge architecture change.)
I've done some searching, but haven't found anyone who is using UIScrollView to do the drawing. Has anyone done this and know of any pitfalls?
All that UIScrollView does when the user scrolls is update its bounds rectangle, which causes the scroll views subviews to move. You could try setting the scroll view's contentMode to UIViewContentModeRedraw. From the docs:
UIViewContentModeRedraw
Redisplays the view when the bounds change by invoking the setNeedsDisplay method.
I'm not really sure, what am I asking...
...but if make layer composites in separate methods, and draw them to the view also outside of the drawRect: method... ...then could my app performance get raised?
I'm actually subclassed a puzzlePiece:UIView class, where the puzzlepiece gets rendered, but the redraw invoked now with setNeedsDisplay since the drawing implementation takes place in the drawRect: method. The app lags.
Can I improve performance if I get rid of the so called drawRect: method?
The thing wants to get implemented: http://gotoandplay.freeblog.hu/
I think what you want to do here is to have a very single, simple UIView that covers the entire play area. Then put each tile in a CALayer, and attach all the CALayers to the UIView and move them around and rotate them. Then there should not need to be a -drawRect: at all. You'll do all your drawing in your layers, and you should get the best performance that way. CALayer is similar to UIView in principle, but much lighter weight (faster and simpler). CALayer is basically a view that can only draw; it can't handle touches or events. It's parent view has to do that for it. But that let's CALayer be much faster.