I enjoyed this read especially! I am one of the creators of 'Stratosphere Multiplayer Defense' (on iPad), and we did it entirely in UIKit. It was very interesting to see what we could get away with, since Apple's OS is very animation-friendly.
We used all UIKit elements like UIViews, UIImageViews, and just core-animationed everything around. It's amazing because you can have 50 towers on screen, all playing their firing animation, along with 100+ monsters walking with their animations.. and it handles well.
The reason we actually chose UIKit (over Cocos2D or OpenGL etc) was (1) my experience in it from app development, but (2) more importantly being able to use Apple's UI widgets. Like scrollviews, gesture-recognizers, etc... rather than try to write our own (I'm sure we've all played iOS games with weird UIs that are kind of like Apple's but fall short). Also, (3) was the speed of getting something working (kind of related to (1) for me).
Downsides are that there's no blending effects like 'additive' for lights/effects, and also it's not trivial to pause a game when it's running timers and core-animation separately (we do have a game loop for logic, but the monsters them-self just move on GPU-based core animations). I'd like to STRESS that using UIKit for most games is probably not a good idea.
Anyway, thought it might interest some people to hear this :)
As an aside: If Apple do transition to a flatter UI (yes, I know it's all rumor and speculation, but humor me), I'd expect to see a reasonable jump in UI responsiveness of older iOS devices.
Not having to load many Retina-sized background images for UI elements would significantly decrease memory pressure on the OS as well as increase responsiveness.
NB. Assuming older devices will be able to upgrade and that texture-heavy UI elements would be phased out.
Same for CSS; I remember when I played around with CSS3[1] many moons ago, and it just slayed my laptop browser at the time.
A lot of people like to use CSS3 for no reason, and it's actually a huge burden on some devices. And that's disregarding the backwards compatibility aspect altogether.
Just as images and assets can be a pain for data plans and low bandwidth, "fanciness" can be a rendering bottleneck.
Only iPhone 4S and iPhone 5 will be upgraded, right? Those have pretty good GPU's. It was iPhone 4 that had a pretty terrible one combined with the retina display at the time.
The biggest impact would be seen on iPads, because iPad 3 actually took a step backwards in terms in performance behind iPad 2, when it switched to retina, and iPad 4 barely matches iPad 2 in performance (if we're comparing native iPad 2 apps vs native "retina" iPad 4 apps).
It could also be the reason why the next iPad is rumored to not have a huge battery anymore, like iPad 3 and iPad 4, and be much slimmer (the last 2 iPads were thicker than iPad 2).
I think the iPhone 4 will get the update, as it's still being sold. (If I had to guess, it's probably selling pretty darn well in the U.S. as it's Verizon's first "free" iPhone.)
I've delegated my iPhone 3GS to extra iPod Touch status. I must say it still holds it's own with speed. Some entry level android phones still can't match it's UI responsiveness. It's a great phone/device.
As someone who still continues to love his 3GS, I have to agree for the most part. Some apps have me considering whether the developers know they run/load that slowly, or whether it's because of my device.
For the most part it is very responsive, but it does get bogged down here and there. For example if I get a text from someone while I'm typing a text to a different person and click the notification ribbon, the phone will hang for a second or two before switching to the other person's text. Is that common on all iOS devices? I'd always imagined it should be much faster given that the Message app is already loaded and active.
It wasn't mentioned in the article, but, it is very important to note: CAGradientLayer is not GPU-backed.
In other words, the comparison is between the CPU redrawing a gradient 60x a second, and, an image being moved around on screen. Not entirely surprising that the one that does less rendering is going to be faster.
Saying "Always rasterize" isn't necessarily the solution, either. Its actually pretty bad advice to follow all the time ;)
I suspect (but can't confirm without the source code to the demo) that shouldRasterize would actually hurt performance in one of the two cases in the article. Specifically, I imagine that it would make the score for adding and removing views from screen even worse than it already is.
Yes, it renders into a bitmap, and reduces CPU usage, once you've paid the upfront rendering cost. But, it doesn't rasterize opacity, and, will use up a ton of memory (because it doesn't result in stretchable images).
Great write-up. I've started recently to compose a lot of table view cells just using subviews, not thinking back to the days where I had to drawRect the subviews. Performance has seemed to catch up, as this article notes. Thanks, Florian!
This is a wonderful article. I've been swapping different methods for performance here and there, but not really settled on a concrete strategy. It looks like drawing definitely has its edge with animations, but for static content CALayers are king. On my weekend project (https://github.com/rnystrom/RNRippleTableView) I found CALayers, especially CAShapeLayers, to be incredibly efficient even during animation. Overriding -drawRect can be fun and rewarding, but its starting to seem like its less and less useful.
I'd love to hear others thoughts on how they use -drawRect nowadays.
I use -drawRect any time I need a UIView's CGContext. For everything else, I use -layoutSubviews.
When you do anything in Core Animation in -drawRect, that throws all drawing on CPU, instead of dividing the work between the CPU and GPU. Which, in one project where I was doing Core Animation alongside OpenGL, that had some very bad consequences.
Core Animation is pretty interesting. Internally, it does some drawing with Core Graphics, but that's only if you use certain features (i.e., use of CAShapeLayers, anything involving shadows)... and even then, CG is pretty darned fast.
I've found CAShapeLayers can be a bit of a resource hog, but since those resources tend to be thrown behind 60 fps animation, that might not be such a problem.
The CAShapeLayer issues only came up when I tried scrolling within a scroll view that contained a CAShapeLayer with a CGPath that had 20 points defining lines. Setting the CAShapeLayer to -shouldRasterize:YES after the animation completed "fixed" the issue, as long as you didn't need to scroll while the CAShapeLayer was animating.
(Don't even bother animating a CAShapeLayer while -shouldRasterize: is set to YES. That just causes even more fun problems with dropped frames.)
Ya setting shouldRasterize = YES would be bassackwards for animating CAShapeLayer. I actually found animating the path attribute incredibly easy and performant with CAKeyframeAnimations.
Another note with -drawRect and animations, I messed around with CADisplayLink and -drawRect to make animations. Beware of doing this, it was a performance hog. It was a simple animation in a large rect, and by the time I had optimized the drawing area, I could have finished the entire project.
I wonder, would CAShapeLayer become more of a resource hog with more points in the path (or more complex arcs), or does it happen with the overall size of the path?
Awesome article. Very evenly balanced in its conclusions, though it avoided the subject of asynchronous rendering/layout. I would add that it is possible to achieve the 1/60th sec. setup time for more complex drawing (NSAttributedStrings, composited views) through the use of background rendering/compositing into CALayers that are delivered to the cells only when complete, though it gets complex very fast, and as noted the performance in newer devices is rapidly getting to the point that I don't have to think so much about this anymore. Now if only we could get everyone to drop the iPhone 4... (just kidding).
I've suspected this was the case but never quantified it, so very cool.
A couple of things I've found work very well are:
1) not creating deep view hierarchies in your table cells - ie. keep those very flat.'
2) Make your images as small as possible in terms of file size. Do background processing to make them smaller and lower the quality if you don't need it. This is by far the easiest way to increase speed if you aren't doing it already.
While its great to have a small image that doesn't require much I/O to read, "small" shouldn't only refer to file size.
Instead, a "small" image should be one that has a small resolution, and is stretchable. This lets the GPU tile the portions of the image that are being stretched and reuse GPU memory, instead of having to waste space in ram to hold duplicate pixel data.
(And when something's as small as it can be, it doesn't minify very well anymore -- not much left to strip from the PNG.)
I was referring to actual images used or featured in the table cell, not tiled backgrounds via UIColor colorWithPatternImage like you mentioning. But yes, it is important to do that as well.
We used all UIKit elements like UIViews, UIImageViews, and just core-animationed everything around. It's amazing because you can have 50 towers on screen, all playing their firing animation, along with 100+ monsters walking with their animations.. and it handles well.
The reason we actually chose UIKit (over Cocos2D or OpenGL etc) was (1) my experience in it from app development, but (2) more importantly being able to use Apple's UI widgets. Like scrollviews, gesture-recognizers, etc... rather than try to write our own (I'm sure we've all played iOS games with weird UIs that are kind of like Apple's but fall short). Also, (3) was the speed of getting something working (kind of related to (1) for me).
Downsides are that there's no blending effects like 'additive' for lights/effects, and also it's not trivial to pause a game when it's running timers and core-animation separately (we do have a game loop for logic, but the monsters them-self just move on GPU-based core animations). I'd like to STRESS that using UIKit for most games is probably not a good idea.
Anyway, thought it might interest some people to hear this :)
Oh, and another benefit is no load screens.