28 October 2015

Theme.swift

I’ve been using (well I think, what is) an interesting pattern in some Swift projects recently. Rather than littering the code with random UIFont, UIColor initializers/magic variables we can define a struct that we’ll use as a container for all of our brand styles.

Defining common app wide constants such as view padding also allows us to remove magic numbers from our autolayout code (I am using SnapKit here)

self.loginButton.snp_makeConstraints { (make) -> Void in
    make.bottom.equalTo(self).offset(-Theme.Padding)
    make.left.equalTo(self).offset(Theme.Padding)
    make.right.equalTo(self).offset(-Theme.Padding)
    make.height.equalTo(60)
}

I also usually let the Theme struct contain my UIAppearance Proxy configuration too. In my applicationDidFinishLaunching I can now add a call to Theme.applyAppearance()

The Template

The standard Theme.swift file I usually start out with:

import Foundation
import UIKit
struct Theme {
/// Common padding for use in autolayout code
static let Padding = 20
/// Common app colors
struct Color {
static let White = UIColor(hex: 0xFFFFFF)
static let Green = UIColor(hex: 0x6EC13A)
static let Gray = UIColor(hex: 0x666666)
static let LightGray = UIColor(hex: 0x7F7F7F)
}
enum Font {
case Light
case Default
case Medium
case Bold
func fontWithSize(size: CGFloat) -> UIFont {
var font: UIFont?
switch (self) {
case .Light:
font = UIFont.systemFontOfSize(size, weight: UIFontWeightLight)
case .Default:
font = UIFont.systemFontOfSize(size, weight: UIFontWeightRegular)
case .Medium:
font = UIFont.systemFontOfSize(size, weight: UIFontWeightMedium)
case .Bold:
font = UIFont.systemFontOfSize(size, weight: UIFontWeightBold)
}
if let font = font {
return font
} else {
print("Font not found")
return UIFont.systemFontOfSize(size)
}
}
}
/// You can also define MotionEffects that you can add to UIViews in your app
/// simply by calling `self.titleLabel.addMotionEffect(Theme.MotionEffects.ForegroundMotionEffectGroup)`
/// Pretty neat!
struct MotionEffects {
/// Motion effect group to be added to all background elements to ensure consistency
static let BackgroundMotionEffectGroup: UIMotionEffectGroup = {
let value = -10
let verticalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y", type: .TiltAlongVerticalAxis)
verticalMotionEffect.maximumRelativeValue = value
verticalMotionEffect.minimumRelativeValue = -value
let horizontalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x", type: .TiltAlongHorizontalAxis)
horizontalMotionEffect.maximumRelativeValue = value
horizontalMotionEffect.minimumRelativeValue = -value
let effectGroup = UIMotionEffectGroup()
effectGroup.motionEffects = [verticalMotionEffect, horizontalMotionEffect]
return effectGroup
}()
}
/// Configure all the UIAppearance Proxies.
static func applyAppearance() {
let titleTextAttributes = [
NSForegroundColorAttributeName: Theme.Color.White,
NSFontAttributeName: Theme.Font.Default.fontWithSize(17)
]
UINavigationBar.appearance().titleTextAttributes = titleTextAttributes
}
}
view raw Theme.swift hosted with ❤ by GitHub

It’s a simple yet effective way to manage all things theme related in one place.

Reading through this I realise that I suck at writing! I’ll get better, eventually.

Will Townsend

Hey 👋 I'm Will Townsend, I hope you enjoyed this post. If you have any questions you can contact me on Twitter, cheers!