This post is going to outline a bunch of tips and tricks to squeeze every last piece of performance out of a UITableView. In a nutshell, what affects your table is how much processing the device needs to perform for each cell requested. If you keep this to a minimum, you will have an extremely smooth scrolling experience.
1. Develop with more cells than you’ll ever need
Whenever I program a table view, I always fill it up with thousands of cells to make sure it performs smoothly. Even if you only ever expect to have a few cells in there, it’s good practice to plan ahead and make a robust table from the beginning that will scale well if required.
If you load any resources from disk or from the web into a cell, cache it in memory. When the image goes off screen and comes back, just pull it back out of the cache for optimal performance.
3. Pre calculate
Pre calculate final NSStrings before hand and store them inside your data objects. Using stringWithFormat, NSNumberFormatter or just even just doing some math can affect your scrolling. Either do it up front in one go or cache the result after the first time and don’t calculate it again. Also if you do need to use a NSNumberFormatter, allocate just one and reuse it over the lifetime of your app, destroying and recreating it is a huge performance hit.
4. Row heights
If all of your rows have the same height, DO NOT use the dataSource method for specifying the height. Instead set the rowHeight property of the table view. If you had 1,000,000 cells and implemented the dataSource method for height, the table would need to call your function a million times to find the total height of all the cells. If you just set the height property, to get the total all it needs to do is multiply the number of rows by a single height value. If your heights are going to be different, then pre calculate the heights (see tip #3).
5. Use reusable identifiers
Probably the first thing any iOS programmer learns when beginning with table views. Still, I sometimes see people that don’t use the identifiers at all or have implemented them incorrectly so it doesn’t do anything anyway. The purpose of this is so that when the user scrolls, the table can take one of the cells that has just gone off screen and shift it to the bottom or top to be reused instead of destroying it and creating a new one. The identifier lets the table know which types of cells are what so if you have different types of cells in one table you can identify them uniquely. This is sometimes the most important factor in ensuring a smooth table.
6. Do it in code
I wrote a blog a while back on why pure code is often better than using interface builder. In terms of performance, creating a cell in 100% code is slightly quicker than loading in a NIB file.
7. Image sizes
Try to make sure the images you’re drawing into a cell is as close to the size of the UIImageView as possible (an exact match preferred). If you try and draw an image that is 1500×1500 pixels into a square that is just 50×50 pixels, a lot of time is needed to resample that image so it can be rendered smaller than the actual size. If you need to do this then take the resizing onto a different thread and then cache the result (tip #2) and load the smaller image into the cell.
8. Deferred loading
When the user quickly flicks the table, cells may only be on screen for a split second. If for example, the cell created an NSURLConnection to get an image from the web, it may be useless because the connection will just need to be cancelled pretty much immediately when the cell gets reused. If the cell wanted data that wasn’t instantly available, I often ‘defer’ loading of that resource by around 0.1 seconds. This way I don’t waste processing power starting up something that I may not need shortly after, and after my short ‘deferment’ period the resource is loaded like normal.
9. Avoid spinners and animations
A UIActivityIndicatorView (or simply known as an ‘activity spinner’) can tear your table view to pieces. Avoid using these. If you have an image that’s loading then I recommend using a static placeholder image instead. This also applies to UIImageView animations (with a sequence of UIImages).
10. Use opaque views where possible
When you set a view to ‘opaque’, the render tree knows that there’s no point drawing anything that is directly behind that view, giving an obvious performance boost.
11. Never touch any of the CALayer properties
The ones I’m talking about are those for rounded corners, outline strokes, drop shadows, etc etc. They may seem convenient but these take a long time to render. Look for an alternative, often a nine patch image drawn over the top works well.
11. If all else fails
Use the time profiler 🙂
Also see my other blog post on developing on lower end devices.