[alert color=”yellow”]Before we begin. I’m starting this year off with open ears. Please take a moment to provide feedback via this short survey. It’ll help me help you.[/alert]

If you have ever dealt with a collection view you can already sense the value of this post. Speed can quickly become an issue if you’re not paying attention. The result is that your customers will let you know in a heartbeat–so will you. You immediately know when a scrollview is not as fast as other apps on your device. Table views are one of the first things every junior iOS developer uses. Feeling lost can happen quickly. This post will dive into a few things that you can look for.

The Turtle & Hare Problem

Table views are an interface object that many apps take advantage of to display structured data. They’re trivial to put in place, which makes them a devious hazard.

The designer is not thinking about performance issues at design time. That designer could even be yourself. Soon you could be dealing with a photo app that needs to show a lot of information within a cell. What may start as a blazingly fast hare can turn into a noticeable turtle. You want your table view to feel smooth—like butter. Those using your app will immediately notice when it isn’t.

Speed up Your Table Views

We’re going to explore these tips by walking through a practical example of a poorly implemented table view.

Often you’ll find a photo app doing several things on an image feed view:

  • Downloading Images (the main content image + user profile images)
  • Updating timestamps
  • Laying out comments
  • Calculating dynamic cell heights

In this example we’re going to focus on a couple of those.

I encourage you to clone the repository to feel how bad things are at the onset. Jump into XMCFeedTableViewCell and walk through each improvement and feel the performance. It’s important to note that if you’re running an iPhone 6+ an improvement may not feel nearly as good. Don’t forget to consider the experience running on a much older phone.

[alert color=”yellow”]github.com project[/alert]

Tip #1 Learn How to Measure Speed

I could write an entire post about instruments. Here I will give you a quick overview because it’s helps quite a bit.

If you don’t have experience with Instruments I urge you to take some time over the weekend to explore a few of them. When you’re trying to measure memory or time usage they will save your tail. While you may not be concerned with performance when you’re just beginning an app you’ll inevitably run into issues down the road when code starts to become a bit of a mess. Refactoring becomes essential. To refactor properly you’ll need to focus your efforts by analyzing performance.

So, if it’s the weekend explore:

  1. Open your project and click Product > Profile
  2. From there select Custom
  3. Find the add button (+) and add the instruments: Allocations, Time Profile, Leaks
  4. Observe your own application & how it performs

For this example, our concern is with speed (but memory is a big issue as well). Which tool do we need? If you said Time Profile you’re right. Let’s open that open it up and observe our app in motion.

Instruments Main View
Here you see a profile of our app. What you’re seeing is me opening the app and scrolling the table up and down as fast as I can. This simulates a decent “worst case scenario” that we can take action on.

Instruments Selected Area
This area is where I begin scrolling the app. We only want to know the time consuming calls during this period.

Instruments Code Area
Now you can start to investigate the code in question. Double click on any of those rows (preferably the rows near the top, where the most time is being consumed).

It’s important to point out that the options selected under Call Tree do not set for you when instruments loads. You’ll need to flip these on yourself.

Tip #2 Avoid Blocking the Main Thread

In the example you’ll notice that the first photo method is blocking the main thread while data is downloaded and converted to an image object. You should always avoid blocking the main thread, but this is especially important with interface object drawing in a collection. Network calls? Keep them in the background (async) and cache responses that come back. You want to avoid re-processing anything. Consider the moment that cells are drawn as dummy time. Your cells should only be displaying data that has already been saved on the device. This will make your life much easier.

Tip #3 Reuse Cells

If you have spent any time in iOS I apologize. But this tip targets those who are new to iOS. You should be using the dequeueReusableCellWithIdentifier to fetch table or collection view cells. If you’re not then you’re wasting an insane amount of time and data.

Tip #4 Cache Downloaded Images

This can easily be the most important tip you’ll read here. If you’re not caching images then you’re experiencing a huge penalty.

If you reuse images locally then take advantage of the UIImage method imageNamed:.

Save time and resources by serving your images in a JPG format. If you’re getting your image from a server you have the luxury of sending the exact image that’s needed. PNG files can be a huge hit in the memory department. If you’re curious jump into the example and change JPG to PNG (the directory and the file name) to download a set of PNG images.

Take advantage of SDWebImage or Heneke to help manage images. In the provided example I’m using Heneke since I’ve never used it and have heard good things about it.

Tip #5 Building Attributed Labels is Expensive

Given all the things you can do with an attributed label, the cost is high. Do what you can to avoid doing doing this outright. Ask yourself i you need attributed text. If so, cache what you can cache.

Tip #6 Cell Height Calculation

If your table has complicated dynamic height needs then cache the calculated heights. Considering how often calculations are made (especially with collection views) you want these to be readily available.

Tip #7 NSDateFormatter Pain

Like attributed text, date formatters can result in a large penalty up front if you’re constantly initializing them. Ideally your web service will provide human readable text on the fly (much easier to calculate at that end). If not then you should create a singleton NSDateFormatter object that you can use. NSDateFormatters used to not be thread safe but that is no longer the case with iOS 7 and later. Thanks to quellish for reminding me of this.

Tip #8 Transparency

If you can avoid it your objects should be opaque (not transparent; can’t see through it). When you have transparent images the system must work much harder to redraw these items. You can actually see negatively affected areas in the simulator by clicking Debug > Color Blended Areas

Simulator with blended layers

Notice the RED? That means those areas are transparent. This is extremely time consuming when you’re dealing with something like a collection view. Ideally, you want to see this screen all green. That may not be feasible for your design so strive to cut the amount of red you see. In the example you can see that the label stretches to the trailing side of the view, which can be cleaned up.

Tip #9 Don’t Bloat Xibs (use Storyboards if you can)

Be careful if you’re using Xibs. When you load a Xib the entire contents are loaded into memory (images! hidden views!). This will not happen with storyboards since they only instantiate what is needed at the time.

There are valuable scenarios where creating xibs make sense. Maybe you’re using a 3rd party library and they decided to write collection interface with code. If you want to use an xib to create a prototype cell you can do that with a xib. Just be cautious about overloading them.

Tip #10 Drop to CoreGraphics

I rarely need to do this, but in the event you need the power you can. Drop down to CoreGraphics and write your UI code in the drawRect function of a view.

Challenge

Who doesn’t love a good challenge? Let’s keep it simple here. Spend some time doing these two things this weekend:

  • Learn how to use instruments (time profiler, allocations)
  • Verify you learned how to use instruments by experimenting with image caching (turn it off, turn it on, observe)

[alert color=”yellow”]github.com project[/alert]

Question & Answer

No questions yet! Leave a comment or write me at david@xmcgraw.com.

Takeaway

Interface performance is important on iOS. This is non-negotiable. If you don’t take the time to solidify your user experience on this device people will run far, far away. You just can’t have an interface that is constantly stuttering while I try to review content.

Your Turn

What is your top tip for handling performance? I’d love to hear about them. Please feel free to share below.

  • Pingback: How To Use The 3 Instruments You Should Be Using()

  • Nick Yu

    drawRect & Core Graphics are evil, they use your CPU not GPU, you are misleading beginners

    • Thanks for the feedback, Nick! In the right hands, and with the right use case(s), they are great. Beginners are highly unlikely to use such a thing (not, at all, trivial to take advantage of), which is why I didn’t dive too deep. I’ll leave it to the reader to explore more, or a future article.

      I’d love to hear more your thoughts! Feel free to share them or other resources you’ve come across!

  • Nick Yu

    AutoLayout is also a very heavy thing on cell layout, I’ve test to calculate height of a cell with Autolayout and do it manually , auto layout takes 2-3 times longer than another.

    • You certainly incur overhead from using Auto Layout, but for most use cases it’ll be negligible.

      If you’re building something like Paper, where the interface is dramatically custom and highly interactive, then staying away from anything that introduces overhead would be wise.

  • Thanks for sharing Nick! I’d also point to this Stack Overflow question (http://stackoverflow.com/questions/14659563/to-drawrect-or-not-to-drawrect-when-should-one-use-drawrect-core-graphics-vs-su) for anyone interested in exploring this more.

  • For basic caching I wouldn’t expect them to be dramatically different (they both take advantage of the same underlying caching system). I’m not entirely sure what else Heneke tries to do with cells.

    I’ve personally gone back to SDWebImage given how active the repository is vs the other. I’d recommend sticking to it and if you really need to iron out cell performance, it may be time to explore AsyncDisplayKit.

  • alireza

    How can I load 25 first cell and then load cells in scrolling TableView ?