16
16
17
17
import ViewEnvironment
18
18
19
+ /// `ViewEnvironmentPropagating` allows an environment propagation node to observe updates to the
20
+ /// `ViewEnvironment` as it flows through the node hierarchy and have
21
+ /// the environment applied to the node.
22
+ ///
23
+ /// For example, for a `UIViewController` hierarchy observing `ViewEnvironment`:
24
+ /// ```swift
25
+ /// final class MyViewController:
26
+ /// UIViewController, ViewEnvironmentPropagating
27
+ /// {
28
+ /// override func viewWillLayoutSubviews() {
29
+ /// super.viewWillLayoutSubviews()
30
+ ///
31
+ /// // You _must_ call this function in viewWillLayoutSubviews()
32
+ /// applyEnvironmentIfNeeded()
33
+ /// }
34
+ ///
35
+ /// func apply(environment: ViewEnvironment) {
36
+ /// // Apply values from the environment to your view controller (e.g. a theme)
37
+ /// }
38
+ ///
39
+ /// // If you'd like to override values in the environment you can provide them here. If you'd
40
+ /// // like to just inherit the context from above there is no need to implement this function.
41
+ /// func customize(environment: inout ViewEnvironment) {
42
+ /// environment.traits.mode = .dark
43
+ /// }
44
+ /// }
45
+ /// ```
46
+ ///
47
+ /// - Important: `UIViewController` and `UIView` conformers _must_ call ``applyEnvironmentIfNeeded()-3bamq``
48
+ /// in `viewWillLayoutSubviews()` and `layoutSubviews()` respectively.
49
+ ///
19
50
public protocol ViewEnvironmentPropagating {
20
51
/// Calling this will flag this node for needing to update the `ViewEnvironment`. For `UIView`/`UIViewController`,
21
52
/// this will occur on the next layout pass (`setNeedsLayout` will be called on the caller's behalf).
@@ -39,6 +70,10 @@ public protocol ViewEnvironmentPropagating {
39
70
/// ``ViewEnvironmentPropagatingObject/environmentAncestorOverride`` property. If no override is present, the
40
71
/// return value will be `parent ?? presentingViewController`/`superview`.
41
72
///
73
+ /// If the value of the ancestor is `nil`, by default, ancestors will not call notify this node of needing an
74
+ /// environment update as it changes. This allows a node to effectively act as a root node when needed (e.g.
75
+ /// bridging from other propagation systems like WorkflowUI).
76
+ ///
42
77
@_spi ( ViewEnvironmentWiring)
43
78
var environmentAncestor : ViewEnvironmentPropagating ? { get }
44
79
@@ -54,34 +89,6 @@ public protocol ViewEnvironmentPropagating {
54
89
///
55
90
@_spi ( ViewEnvironmentWiring)
56
91
var environmentDescendants : [ ViewEnvironmentPropagating ] { get }
57
-
58
- /// The `ViewEnvironment` that is flowing through the propagation hierarchy.
59
- ///
60
- /// If you'd like to provide overrides for the environment as it flows through a node, you should conform to
61
- /// `ViewEnvironmentObserving` and provide those overrides in `customize(environment:)`. E.g.:
62
- /// ```swift
63
- /// func customize(environment: inout ViewEnvironment) {
64
- /// environment.traits.mode = .dark
65
- /// }
66
- /// ```
67
- ///
68
- /// By default, this property gets the environment by recursively walking to the root of the
69
- /// propagation path, and applying customizations on the way back down. You may override this
70
- /// property instead if you want to completely interrupt the propagation flow and replace the
71
- /// environment. You can get the default value that would normally be propagated by calling
72
- /// `_defaultViewEnvironment`.
73
- ///
74
- /// If you'd like to update the return value of this variable and have those changes propagated through the
75
- /// propagation hierarchy, conform to `ViewEnvironmentObserving` and call ``setNeedsEnvironmentUpdate()`` and wait
76
- /// for the system to call `apply(context:)` when appropriate (e.g. on the next layout pass for
77
- /// `UIViewController`/`UIView` subclasses).
78
- ///
79
- /// - Important: `UIViewController` and `UIView` conformers _must_ call
80
- /// ``ViewEnvironmentObserving/applyEnvironmentIfNeeded()-8gr5k`` in `viewWillLayoutSubviews()` and
81
- /// `layoutSubviews()` respectively.
82
- ///
83
- @_spi ( ViewEnvironmentWiring)
84
- var environment : ViewEnvironment { get }
85
92
}
86
93
87
94
extension ViewEnvironmentPropagating {
@@ -99,7 +106,7 @@ extension ViewEnvironmentPropagating {
99
106
/// propagation path, and applying customizations on the way back down. You may override this
100
107
/// property instead if you want to completely interrupt the propagation flow and replace the
101
108
/// environment. You can get the default value that would normally be propagated by calling
102
- /// `_defaultViewEnvironment `.
109
+ /// `_defaultEnvironment `.
103
110
///
104
111
/// If you'd like to update the return value of this variable and have those changes propagated through the
105
112
/// propagation hierarchy, conform to `ViewEnvironmentObserving` and call ``setNeedsEnvironmentUpdate()`` and wait
@@ -111,7 +118,7 @@ extension ViewEnvironmentPropagating {
111
118
/// `layoutSubviews()` respectively.
112
119
///
113
120
public var environment : ViewEnvironment {
114
- _defaultViewEnvironment
121
+ ( self as? ViewEnvironmentObserving ) ? . _environmentOverride ?? _defaultEnvironment
115
122
}
116
123
117
124
/// The default `ViewEnvironment` returned by ``environment``.
@@ -122,15 +129,30 @@ extension ViewEnvironmentPropagating {
122
129
/// You should only need to access this value if you are overriding ``environment``
123
130
/// and want to conditionally return the default.
124
131
@_spi ( ViewEnvironmentWiring)
125
- public var _defaultViewEnvironment : ViewEnvironment {
126
- var environment = environmentAncestor? . environment
127
- ?? . empty
132
+ public var _defaultEnvironment : ViewEnvironment {
133
+ var environment : ViewEnvironment = {
134
+ guard let ancestor = environmentAncestor else {
135
+ return . empty
136
+ }
137
+
138
+ if let observing = ancestor as? ViewEnvironmentObserving {
139
+ return observing. environment
140
+ }
128
141
129
- ( self as? ViewEnvironmentCustomizing ) ? . customize ( environment: & environment)
142
+ return ancestor. _defaultEnvironment
143
+ } ( )
144
+
145
+ if let observing = self as? ViewEnvironmentObserving {
146
+ observing. customize ( environment: & environment)
147
+ }
130
148
131
149
return environment
132
150
}
133
151
152
+ /// Notifies all appropriate descendants that the environment needs update.
153
+ ///
154
+ /// If a descendant's ancestor is `nil` it will not be notified of needing update.
155
+ ///
134
156
@_spi ( ViewEnvironmentWiring)
135
157
public func setNeedsEnvironmentUpdateOnAppropriateDescendants( ) {
136
158
for descendant in environmentDescendants {
0 commit comments