Style vs. Substance. Message vs. Medium. Rhetoric vs. Dialectic.

Is beauty merely skin deep, or is it somehow informed by deeper truths? What does it mean for something to possess good design? Are aesthetic judgments relative, or absolute?

These are deep questions that have been pondered by philosophers, artists, and makers alike for millennia.

And while we all continue our search for beauty and understanding in the universe, the app marketplace has been rather clear on this subject:

Users will pay a premium for good-looking software.

When someone purchases an iPhone, they are buying into Apple’s philosophy that things that work well should look good, too. The same goes for when we choose to develop for iOS—a sloppy UI reflects poorly on the underlying code.

It used to be that even trivial UI customization on iOS required AppStore-approval-process-taunting ju-ju like method swizzling. Fortunately, with iOS 5, developers were given an easier way: UIAppearance.

UIAppearance allows the appearance of views and controls to be consistently defined across the entire application.

In order to have this work within the existing structure of UIKit, Apple devised a rather clever solution: UIAppearance is a protocol that returns a proxy that will forward any configuration to instances of a particular class. Why a proxy instead of a property or method on UIView directly? Because there are non-UIView objects like UIBarButtonItem that render their own composite views. Appearance can be customized for all instances, or scoped to particular view hierarchies:

  • +appearance: Returns the appearance proxy for the receiver.
  • +appearanceWhenContainedIn:(Class <UIAppearanceContainer>)ContainerClass,...: Returns the appearance proxy for the receiver in a given containment hierarchy.

To customize the appearance of all instances of a class, you use appearance() to get the appearance proxy for the class. For example, to modify the tint color for all instances of UINavigationBar:

UINavigationBar.appearance().tintColor = myColor

To customize the appearances for instances of a class when contained within an instance of a container class, or instances in a hierarchy, you use appearanceWhenContainedInInstancesOfClasses(_:) to get the appearance proxy for the class (the older, variadic appearanceWhenContainedIn method has been deprecated):

				.tintColor = myNavBarColor
UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UINavigationBar.self, UIPopoverController.self])
				.tintColor = myNavBarColor
				.tintColor = myNavBarColor
UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UIToolbar.self, UIPopoverController.self])
				.tintColor = myNavBarColor

Determining Which Properties Work With UIAppearance

One major downside to UIAppearance’s proxy approach is that it’s difficult to know which selectors are compatible.

Because +appearance returns an id, Xcode can’t provide any code-completion information. This is a major source of confusion and frustration with this feature.

As of iOS 7, UIAppearance now returns instancetype, which allows for code completion to work as expected. Huzzah!

In order to find out what methods work with UIAppearance, you have to look at the headers:

$ cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/

UIAppearance looks for the UI_APPEARANCE_SELECTOR macro in method signatures. Any method with this annotation can be used with the appearance proxy.

For your convenience, here is the list of properties as of iOS 7.0.

Implementing <UIAppearance> in Custom UIView Subclasses

Much like how NSLocalizedString and #pragma are marks of quality in Objective-C code, having custom UI classes conform to UIAppearance is not only a best-practice, but it demonstrates a certain level of care being put into its implementation.

Peter Steinberger has this great article, which describes some of the caveats about implementing UIAppearance in custom views. It’s a must-read for anyone who aspires to greatness in their open source UI components.

Cocoa developers have a long history of obsessing about visual aesthetics, and have often gone to extreme ends to achieve their desired effects. Recall the Delicious Generation of Mac developers, and applications like Disco, which went so far as to emit virtual smoke when burning a disc.

This spirit of dedication to making things look good is alive and well in iOS. As a community and as an ecosystem, we have relentlessly pushed the envelope in terms of what users should expect from their apps. And though this makes our jobs more challenging, it makes the experience of developing for iOS all the more enjoyable.

Settle for nothing less than the whole package. Make your apps beautiful from interface to implementation.


Questions? Corrections? Issues and pull requests are always welcome.

This article uses Swift version 2.0 and was last reviewed on September 8, 2015. Find status information for all articles on the status page.

Written by Mattt

Mattt (@mattt) is a writer and developer in Portland, Oregon.

Next Article

In C, the scope and lifetime of a variable or function within a program is determined by its storage class. Understanding these storage classes allows us to decipher common incantations found throughout Objective-C