UIKit and GCD

Graphics Bottlenecks

Creating a responsive user interface is one of the most important considerations for a mobile developer, and the smooth scrolling and quick responsiveness of iOS has been one of its hallmarks since day 1. — I’ve gotta be honest here; I still find myself every now and then finding great amusement in just flicking around a simple web view or scrolling some text on my phone. It just feels so right! — Keeping a smooth flow and one to one correspondence between user touch and visual display is crucial for maintaining the illusion and feeling that one is directly interacting with the objects on the screen. One key consideration to make this happen is do not block the main thread. If you are doing anything that might take a significant amount of time, you must do this on a background thread.

With iOS 4.0 and the introduction of blocks and Grand Central Dispatch, it became much easier to complete tasks in the background asynchronously without having to dive into the implementation details of threads and such. If you haven’t yet tried out GCD, take a look at the docs, or check out this tutorial to get you up and running quickly. It’s great for parsing data, downloading from the network, etc. off the main thread, and GCD makes it very easy to write code that will call back into the main thread once the background process completes. What it doesn’t work well for is anything to do with UIKit or the user interface. All drawing and touch interaction takes place, by design, on the main thread. So, what do you do if your drawing itself is taking too long and blocking the main thread? I’m sure there were people much cleverer than me who found some ways to get around it and do some drawing in the background, but basically up until iOS 4, UIKit was not thread-safe at all. If your drawing is too complicated and blocks, then you need to optimize it or simplify it. However, the release notes for iOS 4.0 contain the following short section:

Drawing to a graphics context in UIKit is now thread-safe. Specifically:

  • The routines used to access and manipulate the graphics context can now correctly handle contexts residing on different threads.
  • String and image drawing is now thread-safe.
  • Using color and font objects in multiple threads is now safe to do.

This was not something I had really been interested in or concerned myself with until I ran into just such a problem recently. I created a custom subclass of UILabel which adds a colored, blurred shadow to the label to give it a glow effect. But, this drawing took drastically longer than the regular string drawing. For example, for the drawing that happens at app startup, using regular UILabels takes 104 milliseconds total in drawRect:. To draw the exact same strings with shadows takes 1297 milliseconds! So, you can imagine what this does to frame rates when there are multiple labels being updated rapidly during an already CPU intensive section of the code.

Multi is fun threading!

Since I already know ahead of time exactly what strings I need to display during this particular bottleneck, it would be nice to be able to draw all the labels at once in the background and cache them for later. My first approach was, Continue reading “UIKit and GCD”

Using CGAffineTransform for Circular Layout and Scrolling

Welcome!

Hello, and thanks for stopping by my blog! This marks my first post as part of iDevBlogADay. If you’ve never heard of it, you should go and check it out. It’s basically a group of indie iOS developers who are committed to blogging on a regular basis about all the experiences of being an indie developer – everything from marketing tips, to code examples, to graphic design, to just about anything else you can think of. If you’ve found my blog through iDevBlogADay, then I just want to say a special thanks for stopping by to see one of the “new guys.” Hopefully you’ll find something here that’s helpful or encouraging, no matter what stage you’re in as an indie developer. Just a quick intro to me: I’m a relative newcomer to the developer scene and have been doing iOS development for just over two full years, with no other substantial programming experience prior to that. Currently I’ve got one app for sale on the app store (plus the Lite version) called theDrumDictionary. If you’re interested in that or more info about me and my background, you can find it on the previous posts in this blog. As of right now, this app and basically all my current ideas for future apps are focused on the area of tools for musicians. So if you have a particular interest in music or music apps, be sure to stop back by – or subscribe to the feed – and I’ll try to keep the blog posts coming with everything from specific music and audio programming things I’ve learned, to more widely applicable topics like interface design and code.

Circular Scrollers

For this post, I’d like to share a bit about the major interface control I’m working on for my upcoming metronome app. The basic idea is a rotary control that allows users to select between different values or options by scrolling around in a circle rather than simply scrolling up and down on a UITableView or UIPickerView.

The basic idea is inspired by the beautiful Convertbot by the Tapbots crew. The circular scroller idea itself goes back even further to the many generations of click wheel iPods prior to the rise of the iPhone and multitouch.

Convertbot Interface

It’s a fairly complicated mechanism, and there’s a lot that I could share about the implementation and why I chose this format, but I think I’ll save that for later posts. For now, how about a quick intro to the world of UIViews, CALayers and transforms that form the basis of my version of this control.

Circles are Freakin’ Hard!

If you’ve done any work on interface design on iOS then you’ve surely made use of the UIKit framework. For basic layout and presentation of views, UIView and its various iterations – UIImageView, UIScrollView, etc. – are pretty simple and straightforward. There’s a whole slew of properties you can access to set sizes and positions of views, as well as background color and alpha, and to top it all off, many properties are animatable. With very little code, you can get some pretty complicated looks and have all kinds of things flying in and out, zipping around, fading in and out, growing and shrinking, whatever you want to do. Combine this with the concept of adding views as subviews of others, and you can do all sorts of interesting layouts and animation with minimal effort. The first sort of problem we run into though is the fact that everything, and I mean everything, in the UIKit system is based around rectangles. CGRects to be exact. Every view has a square frame, is positioned within its superview in relation to the superview’s square shape, is defined in terms of rectangular sizes, etc. If you want to deal with circles – or any shape other than a rectangle for that matter – and handle touches, layout, etc. you’re going to have to take on some extra burden to get things looking and acting the way you want. Core Graphics, of course, offers methods for getting circles drawn onto the screen, or you can use pre made images with transparency, but either way, we’re still talking about a circle sitting inside a square UIView. 

The other major problem you’ll notice is that in all of the available properties for UIView, there is no “rotation.” Well, that’s a problem because this whole concept is based on nothing BUT rotation, and laying out views at various angles around a circle. For rotation, we have to turn to the UIView’s “transform” property. This property is of the type CGAffineTransform and allows us to take the underlying view hierarchy and model and transform it in various ways so that when it comes time to draw to the screen, the view has been altered. Views can be translated, scaled, and rotated. Even if you don’t understand the math behind matrix transforms – and I certainly don’t – you can still make good use of the CGAffineTransform using some Apple-provided functions. For rotation there’s CGAffineTransformRotate, which takes an existing transform as it’s first parameter and then rotates it by the angle (in radians, not degrees) given as the second parameter, adding to whatever translation, scale or rotation was already a part of the transform. Or, there’s CGAffineTransformMakeRotation, which will return a new transformation with only rotation at the given angle. Now that we’ve found the functions we need, for this post, we’ll just focus on getting a rudimentary number dial running. It’s not gonna be pretty, but it’ll get the job done. First thing to do is decide how many numbers we want to show and create the views to display them. I’m just going to use UILabels. So here’s what it looks like after the first step of just adding the main circle image to the view and adding 9 number labels as subviews of the circle in the center of the image. I gave the labels a background color to better see what going on, and an alpha of .2 so you can see that right now, it’s 9 labels all right on top of each other in the center. Now, to get them arranged around the circle, we’ll need to apply the rotation transform. Since we’ve got 9 labels, each label will be rotated 2π/9 radians more than the previous one to fill out the entire circle evenly. UIViews rotate around their centers, so in order to arrange the labels around the edge, we can make sure each label is the same width as the circle, then apply the rotation to get this:

Now, it looks OK, but you can easily tell that it’s not ideal. (Unless of course you’re making a kaleidoscope app!) All of these multiple overlapping views mean extra work for the rendering system, not to mention all the just plain waste of having each view extend all the way across the circle.

Cleaning It Up

How can we clean this up? The answer is to dive just a little bit deeper and access the “layer” property of the labels. Every UIView is backed by a CALayer that’s responsible for the actual drawing. UIView adds support for touch handling and presents a different set of API’s to interact with the drawing and animations, but behind every UIView, by default, is a CALayer. If you’ve got a developer login and can access the 2011 WWDC videos, I highly recommend the one called “Understanding UIKit Rendering.” It has a great chart that details some of the shared properties between UIView and CALayer and how they are accessed in each, as well as some of the differences. One property on CALayer that cannot be accessed from UIView is the anchorPoint. The anchorPoint is a CGPoint that defines the exact spot within the view’s own bounds that acts as its anchor to its superview. Confusing right? Maybe this will help: think of the layer as a sheet of paper that you’re pinning to a bulletin board. The anchorPoint is the spot on the piece of paper where the pin will be pushed through. The position property of CALayer is the spot where the pin will be stuck on the bulletin board. 

By default the anchorPoint is set to the center: (0.5, 0.5) – 50% of the way across in both the x and y directions. The exact relationship between UIView’s frame, bounds, center and CALayer’s  bounds, position, and anchorPoint can be difficult to understand at first, but the most important thing for us right now is to know that setting a CALayer’s anchorPoint means any rotation will be applied around that point, again, just like a piece of paper stuck to a cork board will rotate around the pin. So, first, let’s cut the width of our labels in half and position them at the left edge.  Then, we’ll set the anchorPoint to (1.0, 0.5) – the far right side and centered vertically. Now, when we apply the rotation transformation, the labels line up the exact same way as before, but without all the unnecessary size and extra overlapping.

Take out the red color, and we’ve got a decent start to a number dial. If we want to rotate the whole dial, apply a rotation transform on the whole circle image. Since the labels are subviews of the circle, they will rotate right along with it and also maintain their relative positions because of their own rotations. Aren’t transforms fun!!

Two things to note: 1) CALayer also has a transform property, but it’s separate and of a completely different type than the UIView’s transform property. BUT, since the UIView’s drawing is done in its CALayer, we can modify the layer’s anchorPoint property, and still see the results we’re looking for when we rotate the UIView’s transform. It’s all a bit confusing, but very powerful. 2) If you’re not already doing it, in order to access the CALayer API’s you’ll need to link against the QuartzCore framework and import that header in your file. Alright, I think that’s enough for now. Here’s the code on Pastebin for the final version if you’d like to check it out. Add it to viewDidLoad or similar, and don’t forget to clean up the memory (I was lazy for the example)! Happy transforming!

Head to Part 2, for adding rotation based on touch input.

Here’s a look at this concept “in action” as a tempo selector in my app.

The design is far from finished, but you can at least get an idea of what the control looks like. As this is my first iDevBlogADay attempt, I’d love to hear your feedback about the post: too long, too boring, too simple, did I get something wrong? Let me know what you’d like to read about! Thanks!