In this episode I take a stock UITableView and UINavigationController and customize their appearance to match a design for a Starcraft II companion app. We'll leverage Swift enums to capture colors & fonts so we can reuse them in multiple places. We will also utilize the UIAppearance API to style all instances of a UINavigationBar, and get rid of UITableViewCell's default separator indentation.
Episode Links Source Code Setting up Colors and Fonts for Easy Re-use It's not easy sharing colors & font settings between Storyboards and Code, so we'll do the majority of our customization in code. struct Theme { enum Colors { case TintColor case BackgroundColor case DarkBackgroundColor case SectionHeader case Foreground case LightTextColor var color: UIColor { switch self { case .TintColor: return UIColor(red:0.97, green:0.9, blue:0, alpha:1) case .BackgroundColor: return UIColor(hue:0.67, saturation:0.37, brightness:0.35, alpha:1) case .DarkBackgroundColor: return UIColor(red:0.11, green:0.1, blue:0.22, alpha:1) case .SectionHeader: return UIColor(hue:0.67, saturation:0.4, brightness:0.25, alpha:1) case .Foreground: return UIColor(red:0.26, green:0.25, blue:0.37, alpha:1) case .LightTextColor: return UIColor(red:0.64, green:0.65, blue:0.8, alpha:1) } } } enum Fonts { case TitleFont case BoldTitleFont var font: UIFont { switch self { case .BoldTitleFont: return UIFont(name: "Copperplate-Bold", size: 17)! case .TitleFont: return UIFont(name: "Copperplate", size: 16)! } } } } With this in place we can easily reference our colors and fonts from anywhere. Updating the styles for further tweaking is as simple as modifying Theme.swift. Setting Global Styles In AppDelegate.swift: window?.tintColor = Theme.Colors.TintColor.color let navBarAppearance = UINavigationBar.appearance() navBarAppearance.titleTextAttributes = [ NSFontAttributeName: Theme.Fonts.BoldTitleFont.font, NSForegroundColorAttributeName: Theme.Colors.TintColor.color ] navBarAppearance.barStyle = UIBarStyle.Black navBarAppearance.barTintColor = Theme.Colors.Foreground.color Initial Table View Tweaks In viewDidLoad we can set our colors, as well as removing the fake repeating cells that occur at the bottom of a .Plain-styled UITableView: tableView.separatorColor = Theme.Colors.DarkBackgroundColor.color tableView.backgroundColor = Theme.Colors.BackgroundColor.color tableView.tableFooterView = UIView() Custom Race Header View Instead of the standard section headers, we'll implement our own view: class RaceHeaderView : UIView { var imageView: UIImageView! var label: UILabel! convenience init() { self.init(frame: CGRectZero) } override init(frame: CGRect) { super.init(frame: frame) setupSubviews() } required init?(coder: NSCoder) { super.init(coder: coder) setupSubviews() } func setupSubviews() { backgroundColor = Theme.Colors.SectionHeader.color imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = UIViewContentMode.Center addSubview(imageView) imageView.centerYAnchor.constraintEqualToAnchor(centerYAnchor).active = true imageView.leftAnchor.constraintEqualToAnchor(leftAnchor, constant: 20).active = true label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = Theme.Fonts.BoldTitleFont.font label.textColor = Theme.Colors.TintColor.color addSubview(label) label.leftAnchor.constraintEqualToAnchor(imageView.rightAnchor, constant: 20).active = true label.centerYAnchor.constraintEqualToAnchor(imageView.centerYAnchor).active = true } } Then we can vend this view in our controller when our table view asks for it: override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let header = RaceHeaderView() let race = self.tableView(tableView, titleForHeaderInSection: section)! header.label.text = race header.imageView.image = UIImage(named: race.lowercaseString)! return header } override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 66 } Removing the Default Separator Margin I never understood the rationale behind the cell separator indent. It looks okay for a stock look & feel, but often looks wrong when you start changing styles. We can remove this in our custom cell subclass: class GuideCell : UITableViewCell { static let reuseIdentifier = "GuideCell" @IBOutlet weak var nameLabel: UILabel! override func awakeFromNib() { super.awakeFromNib() backgroundColor = Theme.Colors.Foreground.color nameLabel.font = Theme.Fonts.TitleFont.font nameLabel.textColor = Theme.Colors.LightTextColor.color separatorInset = UIEdgeInsetsZero layoutMargins = UIEdgeInsetsZero } } You can also choose to do this in the controller itself, if all cells need to have the same effect applied: func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { cell.separatorInset = UIEdgeInsetsZero cell.layoutMargins = UIEdgeInsetsZero } And with that, we've customized the theme quite a bit! Credits Icons were obtained from the Starcraft Wikia.