UIView background - colorWithPatterOfImage or UIImageView? - ios

I'm wondering, what're the differences or, more importantly, what's better for performance - for a very fast redrawing:
1)myView/myLayer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"myImage.png"]]; for about 40 to 90 views/layers
or
Just using about 40 to 90 UIImageViews ?
What's better for fast redrawing and what's going under the hood, so I can understand which one to pick?
Thanks

This previous post might help: colorWithPatternImage Vs. UIImageView
As well as this: http://cocoaintheshell.com/2011/01/colorwithpatternimage-memory-usage/
Consensus seems to be that colorWithPatternImage uses a lot of memory and to use UIImageView.

Related

UIImageView + UIImage vs CALayer + Content Efficiency

Currently, I have an UIImageView that update its UIImage every few seconds or so. This is an endless process in a very heavy UI.
Given that using CALayers where ever possible over UIView's are always lighter in weight, I am wondering if I convert the UIImageView to CALayer and setting UIImage to setting content of CALayer?
Current
//Every 2 seconds
myImageView.image = UIImage(cgImage: myCGImage)
Suggestion
//Every 2 seconds
myLayer.content = myCGImage
Since UIImageView acts slightly different, I am wondering which method would be more efficient on the CPU/GPU overall.
In general, UIView objects are fairly thin wrappers around CALayers. UIImageView is no exception to that. Most of the "heavy lifting" of decoding and displaying the image is done by the CALayer in both cases, so I doubt if you'll see much difference.
UIImageView is easier to use, and the resulting code is easier to read, so unless you need to do something that requires you to use CALayers, I'd stick with UIKit objects.

Best practice for animated image in iOS - UIImage or UIImageView?

Sorry if the question is a bit subjective, but I couldn't find anything about the topic.
The question is simple:
Which one of the following alternatives are "best", (i.e. best performance). I want to show the image in an UIImageView regardless of the chosen solution.
self.imageView.image = [UIImage animatedImageNamed:#"imagename-" duration:2.0f];
or
self.imageView.animationImages = listOfMyImageNames;
self.imageView.animationRepeatCount = 0;
self.imageView.animationDuration = 2;
[self.imageView startAnimating];
I know that the UIImageView-solution gives more flexibility with number of loops etc, but I want an infinite animation so that doesn't matter in this case.
Of the two options you describe, the animatedImageNamed approach is better than attempting to use animationImages. The reason is that the animationImages approach will crash your device if the series of images is too long or the width and height are too large. The problem is that animationImages will eat up memory at a shocking rate as opposed to the animatedImageNamed API which has a better memory bound. Neither approach is actually the "best" in terms of CPU usage and memory usage as there are significantly better implementations available.

UIScrollView Performance Tips?

I have a UIScrollView that is animating a lot of UIViews (probably too many). Also, these UIViews represent "pages", and sometimes multiple pages are stacked on top of each other, resulting in a "pile" of pages (setup by adding subviews to a given view).
I know that scrolling an excessive number of UIViews can have poor performance, but I was wondering if anybody had some general tips for me to improve performance?
For now, doing drawing manually in drawRect is not something I would like to consider because it would mess up various "page pile" animations. I will keep it in mind for a last resort, but I'd definitely like to avoid it if possible.
Update:
The cause of the performance hit has been determined and is two fold: I'm using antialiasing and shadows on all my UIViews. When I toggle them both off, the performance issues are resolved! However, I obviously don't want to just toggle them off :)
I'm creating my shadows like so:
self.imageView.layer.opaque = YES;
self.imageView.layer.masksToBounds = NO;
self.imageView.layer.shadowOffset = CGSizeMake(-4, 0);
self.imageView.layer.shadowRadius = 2.5;
self.imageView.layer.shadowOpacity = 0.15;
self.imageView.layer.shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(
self.bounds.origin.x,
self.bounds.origin.y,
self.bounds.size.width + 8,
self.bounds.size.height + 2)].CGPath;
Any tips to improve the performance?
As far as antialiasing, it is almost a necessity. The problem is that those "offset pages" are slightly rotated AND my pages have a 1 pixel border. Slightly rotated with a 1 pixel border without antialiasing looks awful. I am simply enabling antialiasing in the .plist by setting "Renders with Edge Antialiasing" to YES.
Any suggestions on how to improve my shadow/antialiasing performance would be appreciated.
How many UIViews are "a lot"? And what kind of views are we talking about?
100 UIViews usually aren't a problem, if they don't require complex drawing.
However, 10 UIWebView instances rendering PDFs are a different story...
Make sure your views are only laid out and drawn if necessary (= only if they're actually visible). You can check this by creating breakpoints in the view's layoutSubviews, for example.
Also, use opaque UIView elements whenever possible. This makes drawing more efficient, as views below opaque elements don't have to be drawn.
If you happen to have custom CALayer instances (e.g. CAShapeLayer with shadows etc.) that require a lot of processing but rarely change, you might want to consider enable rasterization on those: yourLayer.shouldRasterize = YES;
This accelerates drawing by caching the rendered composite image.

How to Efficiently Draw Shadows on Numerous CALayers in iOS?

I've got some card games which use CALayers to draw individual cards. There can easily be 40 or 50 of them on the screen, which usually works fine.
I recently tried to turn on their shadows using the simple properties for CALayers:
theCardLayer.shadowOffset = CGSizeMake(3,2);
theCardLayer.shadowOpacity = 0.7f;
At that point, the program started getting really laggy. Fair enough; some of the docs said that the shadows could be CPU-intensive.
Any ideas for how to efficiently draw shadows on everything? They're all on the same CALayer in the same UIView, so I'm wondering if there might be a way to pull the mask of the layer or its UIView and shadow that, or something ...
Any functionality up to iOS5 is fair game.
At the very least, try setting your layer's shadowPath property. It can make shadow rendering significantly faster.
Kurt offered up the correct solution. Here's an example of how to use a shadowPath:
UIBezierPath *thisCLPath = [UIBezierPath
bezierPathWithRoundedRect:theCardLayer.bounds
cornerRadius:10.0f];
theCardLayer.shadowPath = thisCLPath.CGPath;
Clearly, I'm using rounded corners here. For a straight-edged layer, you can just use bezierPathWithRect:. There are a few other helpful methods in UIBezierPath as well.
The result is just the right side of laggy on older iOS devices (like an iPhone4 or a mid-generation iPod Touch) and blazing on an iPad3.

UIImageView displays MUCH faster than either CG or CALayer. Anyone know why?

I've written an app to test image performance on iOS. I've tried 3 different views, all displaying the same large PNG. The first is a view that draws using CGContextDrawImage(). The second sets self.layer.content. The third is a plain UIImageView.
The image used is created using -[UIImage initWithContentsOfData:] and cached in the viewController. Each test repeatedly allocs a view, adds it to the view hierarchy and then removes it and releases it. Timings are taken from the start of loadView to viewDidAppear and given as fps (effectively, view draws per second).
Here are the results from an iPad 1 running 5.1 using a 912 x 634 unscaled image:
CGContext: 11 fps
CALayer: 10 fps
UIImageView: 430 fps (!)
Am I hallucinating? It seems almost impossible that UIImageView can draw that fast, but I can actually watch the images flicker. I tried swapping between two similar views to defeat possible caching, but the frame rate was even higher.
I had always assumed that UIImageView was just a wrapper for -[CALayer setContent]. However, profiling the UIImageView shows almost no time spent in any drawing method that I can identify.
I'd love to understand what's going on. Any help would be most appreciated.
Here is my idea.
When you modify a UIView hierarchy, by either adding, removing or modifying some view, no actual drawing is performed yet. Instead the view is marked with 'setNeedsDisplay', and is redrawn the next time the runloop is free to draw.
In fact, if your testing code looks something like this (I can only guess):
for (int i=0; i<10000; i++) {
UIImageView* imageView = [[UIImageView alloc] initWithFrame:frame];
[mainView addSubview: imageView];
[imageView setImage: image];
[mainView removeSubview: imageView];
}
than the runloop is blocked until this for loop is done. The view hierarchy is drawn only once, the measured performance is that of allocating and initializing objects.
On the other hand, the CGContextDrawImage() is free to draw right away, I think.
The high FpS with UIImageView is because this does not actually redraws, but only marks the content for redrawing sometimes in the future when UIKit feels like doing it.
As a note:
What is strange though is, that the CGContextmethod is faster than the CALayermethod. I also tried these methods in a project of mine and working with CALayer is the fastest method (except using OpenGL ES of course).
UIImageView uses a different way of rendering images that is much more efficient. You should have a look at this session video from WWDC 2011 that explains how the rendering process works: https://developer.apple.com/videos/wwdc/2011/?id=121

Resources