I'm trying to make sure my UILabel's and UITextView's stay sharp when they exist within a UIScrollView. I came along the 'contentScaleFactor' property on the UIView class, it seems to work perfectly fine on custom drawing but UITextView's and Labels just refuse to redraw themselves at the right contentscale.
http://pastebin.com/PBjhjMbR
Maybe this is due that the actual views that draw the text are a subview of these classes?
You need to set contentScaleFactor recursively on all subviews and also call setNeedsDisplay on each view that you set it on to make them display at higher quality. I have also found that it is not worth setting it on a UIImageView because they won't render at higher quality anyway so there's no point in wasting memory by making their backing layers bigger.
Related
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 am using an AQGridView to display my data in a grid on iPad. Every cell is a UIView subclass and typically, there are 18 cells displayed simultaneously.
I would like to add a round corner to these cells, so I set the cornerRadius property of the relevant layers (i.e. the layer of the main UIView and of one subview). However, this results in performance issues and the scrolling is not smooth any more. When using other CALayer properties, such as shadowOpacity, this does not happen.
Are there any other ways to add a rounded corner (apart from using an image)? Or am I doing something wrong?
I also saw a major performance hit when using cornerRadius on the layer of a view that contained a UIImageView subview. Rasterization solved that problem: view.layer.shouldRasterize = YES;
It could be where you're placing the setCornerRadius call. Make sure it's somewhere that only gets called once, not, for example, in a drawRect method.
My question is very similar to this one Not drawing outside bounds when clipToBounds=NO which received no clear answer.
Basically I have a UIView, and I want to draw a line from the center of it, to the edge of the screen. Calculating where these points are is easy, using [self convertPoint:(CGPoint){0,0} fromView:[self superview]]; (which finds the origin with respect to my view's superview. But when I draw a line from my view's drawRect: it gets clipped at my view's bounds.
Is there a way to draw outside of my view's bounds? I've tried changing the clipsToBounds property, but it doesn't seem to have any effect.
I can't draw my lines from the superview because I need to do this with multiple views and some will be in front of others... figuring out the layer from the superview's drawRect seems like a bad idea.
Similarly, I don't think I can just resize my view's bounds to include the entire screen, because my views need to be dynamically re-sizable... the bounds would have to be HUGE (>20,000 points square) for this to work.
I wouldn't recommend ever drawing outside of a view's bounds. Either your view needs to resize automatically to include your drawing or you need to have transparent overlapping views. Or both. I can't think of a situation that either of these cases wouldn't cover, but I may lack imagination. =)
Likely what is happening currently is that when the super view gets redrawn it tells the super view that it needs redrawn, resulting in erasing the drawing you are doing outside. It's been a while, anyone more knowledgeable can (should!) correct me here if I'm wrong.
I don't know if "Quartz Debug" (from the standard apple developer tools install, /Developer/Applications/Performance Tools/Quartz Debug) works in the simulator, but it's worth a try. It has a mode that will show you when and how often redrawing takes place, with a border and optional delay on the refreshes.
You can do what you are asking, but you need to force redraw your sub-views every time you go outside the sub-view's bounds, meaning that your super-view needs to manually draw it's children inside of it's draw function. Essentially you would be throwing out apple's drawing paradigm and simply causing your sub-views to act like a drawing extension of your main view anyway.
Additionally, if your ranges are so dynamic you may want to consider drawing in percentages of the screen or super-view rather than in points, it may make more sense to code.
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 .
The documentation says that the clipsToBounds property of UIView will clip the drawing to the bounds, or more precisely that the subView can't draw outside of the bounds of the superView.
Sounds nice, but what does that mean in practice?
If I set that to YES, then my subView will automatically only draw those parts which are not outside the bounds of the superView. so it increases the overall performance or do I still have to make sure that I don't create any views that are not visible, i.e. inside a UIScrollView ?
I think it's the opposite: turning on clipping hurts performance because it has to set up a clipping mask. I vaguely remember reading this somewhere, but I can't find it now.
The use case for clipsToBounds is more for subviews which are partially outside the main view. For example, I have a (circular) subview on the edge of its parent (rectangular) UIView. If you set clipsToBounds to YES, only half the circle/subview will be shown. If set to NO, the whole circle will show up. Just encountered this so wanted to share.
The (possible) performance hit is only deterministic if you know the view hierarchy. As mentioned above, usually the renderer will use GPU cycles to draw the view UNLESS some view within the hierarchy uses drawRect:. This does not affect OpenGL ES application because drawRect:is omitted in this type of apps.
From my understanding, determining and displaying the clipped area may take less cycles than actually calculating/drawing/blending the whole view. As of OpenGL ES 2.0 clipping can be calculated in GPU.