Skip to content

Latest commit

 

History

History
132 lines (93 loc) · 4.51 KB

Styling.md

File metadata and controls

132 lines (93 loc) · 4.51 KB

Styling - Styling of UI components

Modern iOS applications often come with custom styling of their UI components. UIKit provides several different approaches for styling your UI. Either you set them explicitly through properties on your views, or you work indirectly with these properties using interface builder or the appearance manager. Form adds programmatic support for styling your UI using style types that allow for easier reuse and modification of your styles.

Styles

Form supports several different kinds of styles, where some are used mainly to compose other styles. You typically pass a style when you construct your UI components:

let label = UILabel(value: "Hello", style: .header)

Many UI components come with default styles, so you can skip passing styles explicitly:

let label = UILabel(value: "Hello")

A UI component's style is often available from a mutable style property as well, allowing convenient updating of styling:

label.style = .footer
label.style.color = .red

By using the restyled helper, new styles can easily be created based on existing styles:

let footStyle = TextStyle.header.restyled { style in
  style.font = ...
  style.color = ...
}

For commonly reused styles we recommend adding them as static extensions:

extension TextStyle {
    static let header = defaultLabel.restyled { ... }
}

And for common restyling, you can add helpers such as:

extension TextStyle {
  func colored(_ color: UIColor) -> TextStyle {
    return restyled { $0.color = color }
  }
}

Form comes with many similar helpers such as the one above allowing for a more declarative restyling:

let footer = TextStyle.header.colored(.blue).resized(to: 24)

Dynamic styles

Some styles might change based on changes in the application environment such as traits changes. For these styles, there is a corresponding DynamicStyle that produces styles based on some dynamic properties such as UITraitCollection. This is true for FormStyle and SectionStyle used for styling table like views such as UITableView, FormView and SectionView.

Dynamic styles can also be restyled:

let customSection = DynamicSectionStyle.default.restyled { style in
  style.header.text.color = .red
}

And if you need to adjust differently based on the traits:

let dynamicSection = customSection.restyledWithStyleAndInput { style, traits in
  let isCompact = traits.horizontalSizeClass = .compact
  style.header.text.resized(to: isCompact ? 14 : 18)
}

Default styling

Many styles and UI components have a default style that will be used when an explicit style is not passed to an initializer. By default, these defaults try to match iOS's system styling. However new defaults can be set up via the DefaultStyling style:

extension DefaultStyling {
  static let custom = DefaultStyling(...)
}

And the default styling can be updated by reassigning defaults:

DefaultStyling.defaults = .custom

You should preferably set up the defaults early on in your applications life cycle.

If you want to support some kind of application theming by updating the defaults while the UI is presented, you have to reload the views (or update their styles explicitly). It is also important to set up your custom styles as computed properties instead of stored, so they will re-evaluate with the currently selected defaults instead of being cached.

Backgrounds

Several UI components such as buttons and sections use images to style their backgrounds. If you want to create these images procedurally, Form provides some helper styles such as BackgroundStyle and SectionBackgroundStyle.

let background = BackgroundStyle(border: BorderStyle(...), color: ...) 
let image = UIImage(image: background)

TextStyle

TextStyle supports the same styling as attributed strings. Not all attributes are exposed as convenience properties though. You can still set these using:

textStyle.setAttribute(letterSpacing, for: .kern)

But preferably you should add helpers instead:

extension TextStyle {
  var letterSpacing: Float {
    get { return attribute(for: .kern) ?? 0 }
    set { setAttribute(newValue, for: .kern, defaultValue: 0) }
  } 
}

Text styles also support registering your own custom string transformations via the TextStyle.registerCustomTransform(). For example, Form provides the textCase transformation and comes with helpers such as uppercased:

let header = TextStyle.default.uppercased