⌘⇧ - Command Shift

iOS development and other nonsense

UIButton Control States

Here’s another example of the (slightly) hidden flexibility of UIButton. Until recently, I was not aware that, in the various setX:forState: methods, you can actually use a bitmask to specify multiple control states. Hopefully I’m not the only person in the world that didn’t realise this and this article will help someone else.

In the documentation for UIControl, control states are described thusly (emphasis mine):

The state of a control; a control can have more than one state at a time. States are recognized differently depending on the control. For example, a UIButton instance may be configured (using the setImage:forState: method) to display one image when it is in its normal state and a different image when it is highlighted.

So why would you care about this? Say you were implementing an edit button or a selection button with text, a background image and an image. You want different text and images for the selected and unselected states (e.g. Edit and Done), but you also want to modify the images or background images when highlighting - if you’re creating your own theme for the app then the default darkening or dimming might not be what you want.

So you actually need four images - normal, highlighted, selected and selected + highlighted.

This is achieved like so:

1
2
3
4
[self.button setBackgroundImage:normal forState:UIControlStateNormal];
[self.button setBackgroundImage:highlighted forState:UIControlStateHighlighted];
[self.button setBackgroundImage:selected forState:UIControlStateSelected];
[self.button setBackgroundImage:selectedHighlighted forState:UIControlStateSelected | UIControlStateHighlighted];

You can do a similar thing with setImage:forState and setTitle:forState: (you need to specifically set a title for UIControlStateSelected | UIControlStateHighlighted, because otherwise the title for UIControlStateNormal will be used. If you’re using different title text attributes, you also need to specifically set them for the selected + highlighted state.

Here’s a spectacularly ugly demonstration of the four states of a button:

UIButton Edge Insets

Don’t subclass or reimplement UIButton!

(Paraphrased from some WWDC talk, I can’t remember which one)

This is good advice. Not only is UIButton a class cluster, so not really suitable for subclassing, it also does a lot for you, and with judicious use of the image view, background image view and (as of iOS6) attributed titles, you can create almost any effect you want.

I’ve recently been struggling with laying out a custom button featuring an image and a label, so if you’ve been baffled by titleEdgeInsets, contentEdgeInsets and imageEdgeInsets, read on!

Creating Individual Layout Constraints

Other posts in the Autolayout series:

In the previous two posts in this series we’ve talked about creating constraints in interface builder and in code using the visual format language. In this final part I’m going to discuss creating individual constraints, and also some category methods on UIView that can make creating layouts in code a lot simpler, and to make your layout code much more readable.

Creating a single constraint is done using this method:

1
2
3
4
5
6
7
+ (id)constraintWithItem:(id)view1
                attribute:(NSLayoutAttribute)attr1
                relatedBy:(NSLayoutRelation)relation
                   toItem:(id)view2
                attribute:(NSLayoutAttribute)attr2
               multiplier:(CGFloat)multiplier
                 constant:(CGFloat)c

Even writing the definition takes several lines, so you can see why this method is not recommended as a way to build your entire layout. However in some cases this method is the only way to build the layout you need, particularly since it exposes the multiplier and constant properties, which are not readily available for the other methods of constraint creation.

Visual Format Language for Autolayout

Other posts in the Autolayout series:

Visual Format Language (VFL) allows the concise building of your layout using an ASCII-art type format string. It’s a powerful tool, but above and beyond the official documentation, there isn’t a lot of information out there. This is going to be an entire post about a single method:

1
2
3
4
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format
                                 options:(NSLayoutFormatOptions)opts
                                 metrics:(NSDictionary *)metrics
                                   views:(NSDictionary *)views

(It’s quite a long method).

Autolayout

Other posts in the Autolayout series:

What is Autolayout?

Autolayout is a new concept for OS X and iOS developers, allowing the developer to specify constraints on the size and position of view elements instead of directly specifying frames and autoresizing masks.

At first glance it appears to be of more benefit to OS X developers, where windows are arbitrarily resizable and tend to contain a more complex subview hierarchy. However, it can also be useful for iOS, to support device rotation, ease development for multiple screen sizes and hopefully, as you’ll learn, make common tasks simpler and easier.

Done right, autolayout can save a lot of time and code, particularly when modifying existing interfaces during design changes. Done wrong, autolayout is a nightmarish journey into the very pits of despair.

Let’s try and take the right path.

Grammar

I’ve recently been looking at Ruby, mostly due to an in-joke at work. It is advertised as a language with a readable, friendly syntax, and it made me think of the syntax and grammar used in modern Objective-C.

Consider the following “simple” example from one of my earlier posts:

1
2
3
4
[myArray enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
    if (indexOfViewToShow != idx)
        view.hidden = YES;
}];

What on earth is going on here? In this post, I will take this code sample apart and explain each character.

Subtle UI Texture in Code

Matt Gemmell has an interesting post on using Photoshop to create subtle noise textures for use on images and controls. With the advent of iOS 6, Apple added support for two key Core Image filter types, CIColorMonochrome and CIRandomGenerator, which allow noise textures to be generated using a few simple lines of code. Resizable images can also be configured to tile the central area instead of stretching it, meaning that noise textures can be used in them without artefacts.

In this post I will recreate the noise textured buttons from Gemmell’s post, without the need for any external image software or image files. Any images for your project that can be generated in code should be, as they lead to smaller packages, simpler retheming and fewer files to manage. I’m not alone in this opinion.

Enumerate Using Block

enumerateObjectsUsingBlock: is often the best way of passing through the contents of a collection. Objective-C fast enumeration (for (... in ...)) uses it under the hood, so it is fast, and you also have the advantage of the index number (for arrays) whilst iterating.

What some people appear not to know is that you don’t have to use id as the object parameter for the enumeration block. You don’t have to call it obj either.

Easy CGGradients

Drawing your own view content can be much better than using images, particularly if you are dealing with universal applications. Once you take into account retina and non-retina, iPad and iPhone, 3.5 and 4 inch you can be managing a plethora of images, and if (when?) any changes are made to the design during development, that’s a lot of files to regenerate and replace in your project.

Lots of common designs can be easily drawn in code, but the core graphics API for drawing gradients is a little bit impenetrable, particularly if you are used to Objective-C as opposed to C.