We just pushed a new design for Ruby Science. Previous purchasers and Prime subscribers can grab the update on Learn.
The new design includes a new layout, which is much more appropriate for a book. We updated the typeface and font size to improve the experience when reading the book on a screen as opposed to a printed page. The margin size is also greatly reduced, making it easier to zoom in on a laptop screen and bring the text right to the edges.

Get your copy of Ruby Science today.
We recently announced the launch of our subscription service: Learn Prime.
For just $99/month, you get ongoing access to everything we teach, including books like Ruby Science. You’ll even get access to all our in-person and online workshops. Get access to exclusive subscriber content, as well as access to our private Campfire room, where you can get live help from thoughtbot designers and developers.
Let’s say that we have an app that makes use of image assets for icons, custom progress bars, etc. Now we want to allow users to theme the app, and these images need to conform to this new color scheme. The obvious solution is to add 1x and 2x versions of every single possible image asset in the app, right?
No, of course not. Duplication is bad, and duplicating images is just another form of duplication. But we’re clever folks, I’m sure we can find a nice way around this!
We’ll start out with a simple image that we need to tint to our new color scheme. It has an alpha channel, but it’s a flat color and the new color is going to be flat as well.

The smart way to do this would be to redraw the image using a new color using CoreGraphics. Let’s consider the steps needed to make this happen:
Looks pretty straightforward, but retaining the alpha channel will likely be the trickiest part of the exercise. Looking into the UIImage documentation, we know we’re going to want to use -drawInRect:blendMode:alpha: to draw the new image. Looking at the available blending modes leaves us a little… confused:
kCGBlendModeSourceAtop
R = S*Da + D*(1 - Sa)
Available in iOS 2.0 and later.
Declared in CGContext.h.
That’s not actually super helpful. Let’s keep reading, I guess.
The blend mode constants introduced in OS X v10.5 represent the Porter-Duff blend modes.
The symbols in the equations for these blend modes are:
R is the premultiplied result
S is the source color, and includes alpha
D is the destination color, and includes alpha
Ra, Sa, and Da are the alpha components of R, S, and D
Ah, ok. So we need to do some translation here. What we’re looking for is a blending mode that will draw the new color (Destination, represented by D) using the alpha components from the source (represented by Sa). Looking through the available options armed with this new knowledge, we find one that looks promising:
kCGBlendModeDestinationIn
R = D*Sa
Available in iOS 2.0 and later.
Declared in CGContext.h.
That looks like it’s using the right components, but there’s only one way to be completely sure. Let’s get ready for some good old fashioned trial and error. First, we’ll go ahead and create a category on UIImage:
// UIImage+Tint.h
@interface UIImage (Tint)
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor;
@end
This feels like a nice interface. We can assume that we will either have the image that needs tinting, or we can simply chain it together with -imageNamed: to grab the image from the bundle. Now for the initial implementation:
// UIImage+Tint.m
#import "UIImage+Tint.h"
@implementation UIImage (Tint)
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor
{
// It's important to pass in 0.0f to this function to draw the image to the scale of the screen
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
@end
Plugging this into our app is as simple as [[UIImage imageNamed:@"ralph"] tintedImageWithColor:[UIColor thoughtbotRed]];

That was simple enough. But what if we want to retain a gradient from the image? Let’s say we have a nice grayscale button that should be skinned as well:

All we have to do is pass it through our tint method, and voila!

Oops. Looking back at the blending mode, this won’t work at all. The only things that kCGBlendModeDestinationIn takes into account are the destination color, and the source alpha. We’re losing all of the grayscale info from the source image. Looks like we still have more work to do. Once again, we turn to the documentation, and find kCGBlendModeOverlay:
kCGBlendModeOverlay
Either multiplies or screens the source image samples with the background image samples,
depending on the background color. The result is to overlay the existing image samples
while preserving the highlights and shadows of the background. The background color mixes
with the source image to reflect the lightness or darkness of the background.
Available in iOS 2.0 and later.
Declared in CGContext.h.
That sounds like it should tint our gradient nicely. But we don’t want to change the current behavior. This calls for some refactoring. The new interface becomes:
// UIImage+Tint.h
@interface UIImage (Tint)
- (UIImage *)tintedGradientImageWithColor:(UIColor *)tintColor;
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor;
@end
And the new implementation:
// UIImage+Tint.m
#import "UIImage+Tint.h"
@implementation UIImage (Tint)
#pragma mark - Public methods
- (UIImage *)tintedGradientImageWithColor:(UIColor *)tintColor
{
return [self tintedImageWithColor:tintColor blendingMode:kCGBlendModeOverlay];
}
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor
{
return [self tintedImageWithColor:tintColor blendingMode:kCGBlendModeDestinationIn];
}
#pragma mark - Private methods
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor blendingMode:(CGBlendMode)blendMode
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
@end
This gets us closer, but you can see that now we’ve lost our alpha information.

Since kCGBlendModeOverlay doesn’t take the source alpha into account, we need to try to get that back. There’s a very simple way that we can accomplish this. We already know that kCGBlendModeDestinationIn` draws the destination image in the source alpha, so we can conditionally redraw the image using this mode:
// UIImage+Tint.m
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor blendingMode:(CGBlendMode)blendMode
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn)
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
@end
And we finally have the desired image:

This image is perfect to assign to a static variable to allow it to be used app-wide, without having to redraw.
By using CoreGraphics to tint our images for us, we open ourselves up to a much wider range of functionality in our apps, and we get the added benefit of smaller bundle sizes, and reduced asset duplication.
Be sure to check out the many different blending modes available in Apple’s documentation. You can check out the sample app that includes the code used here on GitHub.
This week on the podcast, Ben Orenstein is joined by Jarrod Drysdale, the author of Bootstrapping Design. Ben and Jarrod discuss the sales and revenue of the book, and his new project, cascade.io. They also talk about learning new things, problem solving, and the differences between programming and design. They also discuss the downside to recurring revenue, successful marketing strategies for his book, advice for people who want to start something new, the concerns of a solo entrepreneur, and how his previous failures help him keep perspective.
We have two online workshops scheduled for January, 2013. If you’ve always wanted to take one of our workshops, but couldn’t travel to Boston or SF, these online versions will give you the same great education in a format custom-tailored to the online experience.
Design for Developers. This workshop is for developers who want to better understand the language and practice of visual design. This workshop starts January 4th, 2013.
Test-Driven Rails. By the end of the workshop, you will know how to do Test-Driven Development using RSpec and Capybara. This workshop starts January 11th, 2013.
Each of the workshops lasts 4 weeks and is a mix of pre-recorded lessons with extended periods of hands-on practice, one-on-one support, and group online office hours. Signing up today gets you the first video lesson while you wait for the workshop to start.
After the course ends, you’ll still have access to the course videos, instructor, and thoughtbot team who will answer any ongoing questions you have about the topic. You’ll also have access to any updates we make to the course in the future.
And as with everything at learn.thoughtbot.com, if you’re not happy, just let us know within 30 days and we’ll refund your money. It’s as simple as that.
I hope we’ll see you in one of the courses!
In the previous article, we explored different techniques to customize the look and feel of UIButton, assigning to each a difficulty level based on the complexity of the Objective-C code involved in the implementation. What I intentionally left out mentioning however, is that some of these methods come with non-trivial performance ramifications that should be taken into consideration when choosing one over another.
In order to understand how performance is affected, we need to have a closer look at the technology stack behind graphics in iOS. This block diagram represents the different frameworks and libraries and how they relate to each other:

In the topmost layer, there is UIKit—a high-level Objective-C framework that manages the graphical user interface in iOS. It is made up of a collection of classes, each corresponding to a specific UI control such as UIButton and UILabel. UIKit itself is built on top of Core Animation, a framework introduced in OS X Leopard and ported to iOS to power the smooth transitions that it later became known for.
Deeper in the stack we have OpenGL ES, an open-standard library for rendering 2D and 3D computer graphics on mobile devices. It is widely used for game graphics and powers both Core Animation and UIKit. The last piece in the software stack is Core Graphics—historically referred to as Quartz—which is a CPU-based drawing engine that made its debut on OS X. These two low-level frameworks are both written in the C programming language.
The bottom row in the diagram represents the hardware stack, composed of the the graphics card (GPU) and the main processor (CPU).
We talk about hardware acceleration when the GPU is used for compositing and rendering graphics, such as the case for OpenGL and the Core Animation/UIKit implementations built on top of it. Until recently, hardware acceleration was a major advantage that iOS held over Android; most animations in the latter felt noticeably choppier as a result of its reliance on the CPU for drawing.
Offscreen drawing on the other hand refers to the process of generating bitmap graphics in the background using the CPU before handing them off to the GPU for onscreen rendering. In iOS, offscreen drawing occurs automatically in any of the following cases:
drawRect() method, even with an empty implementation.shouldRasterize property set to YES.setMasksToBounds) and dynamic shadows (setShadow*).UIViewGroupOpacity).As a general rule, offscreen drawing affects performance when animation is involved. You can inspect which parts of the UI are being drawn offscreen using Instruments with an iOS device:



Update: As Alex pointed out in the comments, you can also inspect offscreen rendering by checking the Debug > Color Offscreen-Rendered option in the iOS Simulator. Unless you are doing performance tests—which was the case here—using the simulator is the easiest and most straightforward way to inspect offscreen rendering.

Let’s now have a look at the performance footprint of each of the previously introduced approaches.
Customizing our button with a UIImage background relies entirely on the GPU for rendering the image assets saved on disk. The resizable background image variant is considered the least resource-hungry approach since it results in smaller app bundles and takes advantage of hardware acceleration when stretching or tiling pixels.
The CALayer-based method we implemented requires offscreen-drawing passes as it uses masking to render rounded corners. We also had to explicitly disable the animation that comes turned on by default when using Core Animation. Bottom line, unless you need animated transitions, this technique is not adequate for custom drawing.
The drawRect method relies on Core Graphics to do the custom drawing, but its main drawback lies in the way it handles touch events: each time the button is pressed, setNeedsDisplay forces it to redraw; not only once, but twice for every single tap. This is not a good use of CPU and memory, especially if there are multiple instances of our UIButton in the interface.
So, does this mean that using pre-rendered assets is the only viable solution? The short answer is no. If you still need the flexibility of drawing with code, there are techniques to optimize your code and reduce its performance footprint. One way is to generate a stretchable bitmap image and reuse it across all instances.
We’ll start by creating a new subclass of UIButton following the same steps detailed in the previous tutorial, then we’ll define our class-level static variables:
// In CBHybrid.m
#import "CBHybrid.h"
@implementation CBHybrid
// Resizable background image for normal state
static UIImage *gBackgroundImage;
// Resizable background image for highlighted state
static UIImage *gBackgroundImageHighlighted;
// Background image border radius and height
static int borderRadius = 5;
static int height = 37;
Next we will move our drawing code from drawRect in CBBezier to a new helper method, with a couple of changes: we will generate a resizable image instead of a full-sized one, then we will save the output to a static variable for later reuse:
- (UIImage *)drawBackgroundImageHighlighted:(BOOL)highlighted {
// Drawing code goes here
}
First, we need to get the width of our resizable image. For optimal performance, we want a 1pt stretchable area in the vertical center of the image.
float width = 1 + (borderRadius * 2);
The height matters less in this case, as long as the button is tall enough for the gradient to be visible. The value of 37pt was picked to match the height of the other buttons.
Moving on, we need a bitmap context to draw into, so let’s create one:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Setting the second boolean argument to NO will ensure that our image context is not opaque. The last argument is for the scale factor (screen density). When set to 0.0 it defaults the scale factor of the device.
The next block will be exactly like our previous Core Graphics implementation in CBBezier, save for updated values and the use of the highlighted argument instead of the default self.highlighted property:
// Gradient Declarations
// NSArray *gradientColors = ...
// Draw rounded rectangle bezier path
UIBezierPath *roundedRectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, width, height) cornerRadius: borderRadius];
// Use the bezier as a clipping path
[roundedRectanglePath addClip];
// Use one of the two gradients depending on the state of the button
CGGradientRef background = highlighted? highlightedGradient : gradient;
// Draw gradient within the path
CGContextDrawLinearGradient(context, background, CGPointMake(140, 0), CGPointMake(140, height-1), 0);
// Draw border
// [borderColor setStroke...
// Draw Inner Glow
// UIBezierPath *innerGlowRect...
The only step we will need to add compared to CBBezier is a method that saves the output in a UIImage and a call to UIGraphicsEndImageContext to clean up after us.
UIImage* backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup
UIGraphicsEndImageContext();
Now that we have a method to generate our background images, we will have to implement a common initializer method that will instantiate these images and set them up as the background for our CBHybrid instance.
- (void)setupBackgrounds {
// Generate background images if necessary
if (!gBackgroundImage && !gBackgroundImageHighlighted) {
gBackgroundImage = [[self drawBackgroundImageHighlighted:NO] resizableImageWithCapInsets:UIEdgeInsetsMake(borderRadius, borderRadius, borderRadius, borderRadius) resizingMode:UIImageResizingModeStretch];
gBackgroundImageHighlighted = [[self drawBackgroundImageHighlighted:YES] resizableImageWithCapInsets:UIEdgeInsetsMake(borderRadius, borderRadius, borderRadius, borderRadius) resizingMode:UIImageResizingModeStretch];
}
// Set background for the button instance
[self setBackgroundImage:gBackgroundImage forState:UIControlStateNormal];
[self setBackgroundImage:gBackgroundImageHighlighted forState:UIControlStateHighlighted];
}
We’ll proceed by setting the button type to custom and implementing initWithCoder (or initWithFrame if the button instance is created in code):
+ (CBHybrid *)buttonWithType:(UIButtonType)type
{
return [super buttonWithType:UIButtonTypeCustom];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setupBackgrounds];
}
return self;
}
To make sure that the new subclass is working properly, duplicate one of the buttons in Interface Builder and change its class to CBHybrid. Change the button content to CGContext-generated image then build and run.

The full subclass code can be found here.
When all is said and done, pre-rendered assets would still perform better than any code-based solution. Then again, there is much to gain in terms of flexibility and efficiency once Core Graphicsis tamed—that and a hybrid approach like the one we just covered would not affect performance to any noticeable degree on today’s hardware.
Update: Andy Matuschak, a member of the UIKit team, was once again kind enough to provide more clarifications about offscreen rendering as well as some good insights about cache-purging in the comments section.