Styling an iOS app

Last sprint I spent way too many hours aligning the native UI with the design. These were my top #3 time sinkers:

  1. Silent copy/design changes. I missed a few because we didn’t have a procedure to communicate changes.
  2. Different device sizes. A mockup per size is not cost effective for us, so I test on the 4s (smallest device we support) and use common sense. Overlapping elements need autolayout constraints with different priorities, which is extra work.
  3. Unintended design variations. A design should be consistent, not have wildly different screens, colors, fonts, sizes, etc. But often, whether by human error, color spaces, or some reason, new elements sneak in. This is greatly mitigated by writing down a reference, which is also the subject of this post.

A visual style guide is the documentation of your design. It enumerates colors, fonts, layout, logos, and any other element related to your brand identity.

Why do you need a style guide?

To promote consistency

  • Consistency let users create a mental model to navigate our work. And viceversa, inconsistent design hampers understanding, and is perceived as unprofessional, sloppy, and less reliable.
  • Prevents accidental variations and facilitates testing.

To guide future design work

  • It communicates your mission statement and brand essence.
  • It provides basic constraints so you can focus on more creative work.
  • It brings new team members up to speed.

To promote modular thinking. The bigger your app, the most you will benefit from having a set of reusable and styleable building blocks. This in itself offers a few benefits:

  • Work can be split between different teams.
  • Style is flexible because stylesheets exist separately.
  • Components are created with reusability in mind (instead refactored later).

How to style UI components?

There are several technologies that may help, like Dynamic Type and UIAppearance. But essentially you need to write a set of visual attributes per style. Switching themes should require one line of code.

Step one: enumerate your assets. It is faster and safer than typing each name multiple times. If generating code is your thing, use Swiftgen, or R.swift. I use my own code for colors and fonts, plus Paintcode for images. Click the images for code.

public struct Fonts  
{
    enum SF: String
    {
        case bold    = "SanFranciscoRounded-Bold"
        case regular = "SanFranciscoRounded-Regular"

        func size(_ size: CGFloat) -> UIFont {
            return UIFont(name: self.rawValue, size: size)!
        }
    }
}

let sf = Fonts.SF.regular.size(16.0)

// ---

enum Colors: Int64  
{
    case black = 0x000000ff
    case white = 0xffffffff

    func color() -> UIColor
    {
        let red   = CGFloat((self.rawValue >> 24) & 0xff) / 255.0
        let green = CGFloat((self.rawValue >> 16) & 0xff) / 255.0
        let blue  = CGFloat((self.rawValue >>  8) & 0xff) / 255.0
        let alpha = CGFloat((self.rawValue      ) & 0xff) / 255.0
        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }
}

extension UIColor  
{
    func highlight(withLevel highlight: CGFloat) -> UIColor {
        // ...
    } 
}

let blue5 = Colors.white.color().highlight(withLevel: 0.95)  

Step two: create a style set per element. Keep in mind that different UIKit elements have different sets of properties, and a given element may be reused for different purposes. The quickest and simplest solution I can think of is using a struct with a closure per style.

protocol Style  
{
    var headline: (UILabel)->() { get }
    // ... any reusable element in your UI
}

struct OrangeStyle: Style  
{
    let headline: (UILabel)->() = { label in
        label.font = Fonts.SF.regular.size(16.0)
    }
}

protocol Styleable {}

extension Styleable {  
    func apply(_ style: (Self)->())->Self {
        style(self)
        return self
    }
}

extension UILabel: Styleable {}

let style: Style = OrangeStyle()  
let title = UILabel().apply(style.headline)  

The downside of using closures instead sets of fixed attributes, is that with the later you have a guide of which attributes to apply. The advantage is that closures are more flexible.

If you find yourself writing a different style for each screen you’ll get little reward from styling. This is most likely a design flaw. Marissa Mayer said:

Count a point for every different font, font size, and color on a page. Don't go beyond five points.

Other implementations

Stylish

Stylish lets you style your app using JSON files. Or programmatically, but I’d say it’s trading off complexity for JSON support.

JSON files can be updated by non designers, downloaded, and changed without recompiling. However they are type unsafe (no compiler checking), require that you deal with a JSON file format, and complicate the code.

The implementation uses complete sets of properties (example) for each UIKit element. Take a look if you are interested:

Stylesheet

Not my cup of tea, but it sure was an interesting reading!

The following code is part of a sample style. It changes the font of the UILabels marked with the marker protocol BodyFontStyle.

Style<BodyFontStyle, UILabel> { $0.font = p.font.body }  

Given a custom class TitledSwitchView this is how you apply the styles:

final class TitledSwitchView: UIView {  
    final class TitleLabel: UILabel {}
    // ...
}
extension TitledSwitchView: RoundedCornersStyle, LightBorderStyle {}  
extension TitledSwitchView.TitleLabel: BodyFontStyle {}  

The autoapply code is the fun part. It shows two alternative ways of injecting a method into every UIView instance, either using swizzling or the UIAppearance proxy. The view will be pattern matched here against the pair marker/subclass defined in the style.

Finally, an associated object prevents applying a style more than once when the appearance proxy or View.didMoveToWindow are triggered.

Chameleon

Hardcoding the styles is a deal breaker for me, but there is a lot of interesting code in Chameleon. For instance, this method returns a status bar (default or light) contrasting with the navigation bar background.

References

Visual Style Guide Resources

Examples

comments powered by Disqus