Providing data to the view hierarchy with custom traits
Share data that needs to flow hierarchically across multiple levels of your view hierarchy.
Overview
A custom trait is a type that conforms to UITraitDefinition. Use a custom trait to pass application data, such as a color theme that a person selects in your app, through the objects in your view hierarchy that adopt UITraitEnvironment. Represent data with a custom trait when you want to:
Propagate data to many children, such as contained view controllers or subviews.
Pass data to distant components; for example, from a UIWindowScene subclass to a view inside a stack of view controllers.
Provide context about the environment, such as information about a containing view controller from the current view controller, without a dependency on the containing view controller.
Avoid using custom traits for data that you can pass directly between view controllers and views.
Create, set, and access a custom trait
To create a custom trait, define a type that conforms to UITraitDefinition:
struct ContainedInSettingsTrait: UITraitDefinition {
static let defaultValue = false
}defaultValue is the only required static property. The system infers the type for your custom trait from the type of the value you set for defaultValue. After you define your custom trait, set its value using your trait as the key in the traitOverrides property of an object in your view hierarchy:
self.traitOverrides[ContainedInSettingsTrait.self] = trueThen, the system propagates the trait and value to that object’s descendants in the view hierarchy. For example, if the object is a view controller, the system propagates the view controller’s trait collection with the override to the view controller’s view and subviews, and to any child view controllers.
Access the value of your trait using your trait as the key in a trait collection:
let value = traitCollection[ContainedInSettingsTrait.self]Simplify custom trait access with extensions
Add convenience properties to UITraitCollection and UIMutableTraits to make your custom trait easier to access. First, extend UITraitCollection with a property to get the value of your custom trait from a trait collection:
extension UITraitCollection {
var isContainedInSettings: Bool { self[ContainedInSettingsTrait.self] }
}Next, extend UIMutableTraits to get and set the value of your custom trait:
extension UIMutableTraits {
var isContainedInSettings: Bool {
get { self[ContainedInSettingsTrait.self] }
set { self[ContainedInSettingsTrait.self] = newValue }
}
}Then, use standard property syntax to access and update your custom trait:
let traitCollection = UITraitCollection { mutableTraits in
mutableTraits.isContainedInSettings = true
}
let value = traitCollection.isContainedInSettingsEnhance custom trait interactions
Set optional properties of UITraitDefinition to give your trait additional system capabilities and improve debugging:
- affectsColorAppearance
Set this to true when you use your custom trait to implement custom dynamic colors.
- name
Set this to a string value to identify your custom trait in the debugger.
- identifier
Set this to a string value to uniquely identify your custom trait, which makes it eligible for additional features such as encoding. Use reverse DNS format to make your identifier globally unique in your app.
The following example demonstrates setting these optional properties:
enum MyAppTheme: Int {
case standard, pastel, bold, monochrome
}
struct MyAppThemeTrait: UITraitDefinition {
static let defaultValue = MyAppTheme.standard
static let affectsColorAppearance = true
static let name = "Theme"
static let identifier = "com.myapp.theme"
}