@@ -97,14 +97,15 @@ private final class ModeledHostingController<Model, Content: View>: UIHostingCon
97
97
var swiftUIScreenSizingOptions : SwiftUIScreenSizingOptions {
98
98
didSet {
99
99
updateSizingOptionsIfNeeded ( )
100
-
101
- if !hasLaidOutOnce {
100
+ if isViewLoaded {
102
101
setNeedsLayoutBeforeFirstLayoutIfNeeded ( )
103
102
}
104
103
}
105
104
}
106
105
107
106
private var hasLaidOutOnce = false
107
+ private var maxFrameWidth : CGFloat = 0
108
+ private var maxFrameHeight : CGFloat = 0
108
109
109
110
init (
110
111
setModel: @escaping ( Model ) -> Void ,
@@ -132,10 +133,9 @@ private final class ModeledHostingController<Model, Content: View>: UIHostingCon
132
133
override func viewDidLoad( ) {
133
134
super. viewDidLoad ( )
134
135
135
- // `UIHostingController`'s provides a system background color by default. In order to
136
- // support `ObervableModelScreen`s being composed in contexts where it is composed within another
137
- // view controller where a transparent background is more desirable, we set the background
138
- // to clear to allow this kind of flexibility.
136
+ // `UIHostingController` provides a system background color by default. We set the
137
+ // background to clear to support contexts where it is composed within another view
138
+ // controller.
139
139
view. backgroundColor = . clear
140
140
141
141
setNeedsLayoutBeforeFirstLayoutIfNeeded ( )
@@ -146,21 +146,31 @@ private final class ModeledHostingController<Model, Content: View>: UIHostingCon
146
146
147
147
defer { hasLaidOutOnce = true }
148
148
149
- if #available( iOS 16 . 0 , * ) {
150
- // Handled in initializer, but set it on first layout to resolve a bug where the PCS is
151
- // not updated appropriately after the first layout.
152
- // UI-5797
153
- if !hasLaidOutOnce,
154
- swiftUIScreenSizingOptions. contains ( . preferredContentSize)
155
- {
156
- let size = view. sizeThatFits ( view. frame. size)
157
-
158
- if preferredContentSize != size {
159
- preferredContentSize = size
160
- }
161
- }
162
- } else if swiftUIScreenSizingOptions. contains ( . preferredContentSize) {
163
- let size = view. sizeThatFits ( view. frame. size)
149
+ if swiftUIScreenSizingOptions. contains ( . preferredContentSize) {
150
+ // Use the largest frame ever laid out in as a constraint for preferredContentSize
151
+ // measurements.
152
+ let width = max ( view. frame. width, maxFrameWidth)
153
+ let height = max ( view. frame. height, maxFrameHeight)
154
+
155
+ maxFrameWidth = width
156
+ maxFrameHeight = height
157
+
158
+ let fixedSize = CGSize ( width: width, height: height)
159
+
160
+ // Measure a few different ways to account for ScrollView behavior. ScrollViews will
161
+ // always greedily fill the space available, but will report the natural content size
162
+ // when given an infinite size. By combining the results of these measurements we can
163
+ // deduce the natural size of content that scrolls in either direction, or both, or
164
+ // neither.
165
+
166
+ let fixedResult = view. sizeThatFits ( fixedSize)
167
+ let unboundedHorizontalResult = view. sizeThatFits ( CGSize ( width: . infinity, height: fixedSize. height) )
168
+ let unboundedVerticalResult = view. sizeThatFits ( CGSize ( width: fixedSize. width, height: . infinity) )
169
+
170
+ let size = CGSize (
171
+ width: min ( fixedResult. width, unboundedHorizontalResult. width) ,
172
+ height: min ( fixedResult. height, unboundedVerticalResult. height)
173
+ )
164
174
165
175
if preferredContentSize != size {
166
176
preferredContentSize = size
@@ -175,10 +185,6 @@ private final class ModeledHostingController<Model, Content: View>: UIHostingCon
175
185
}
176
186
177
187
private func updateSizingOptionsIfNeeded( ) {
178
- if #available( iOS 16 . 0 , * ) {
179
- self . sizingOptions = swiftUIScreenSizingOptions. uiHostingControllerSizingOptions
180
- }
181
-
182
188
if !swiftUIScreenSizingOptions. contains ( . preferredContentSize) ,
183
189
preferredContentSize != . zero
184
190
{
@@ -187,7 +193,7 @@ private final class ModeledHostingController<Model, Content: View>: UIHostingCon
187
193
}
188
194
189
195
private func setNeedsLayoutBeforeFirstLayoutIfNeeded( ) {
190
- if swiftUIScreenSizingOptions. contains ( . preferredContentSize) {
196
+ if swiftUIScreenSizingOptions. contains ( . preferredContentSize) , !hasLaidOutOnce {
191
197
// Without manually calling setNeedsLayout here it was observed that a call to
192
198
// layoutIfNeeded() immediately after loading the view would not perform a layout, and
193
199
// therefore would not update the preferredContentSize in viewDidLayoutSubviews().
@@ -203,17 +209,4 @@ private final class ModeledHostingController<Model, Content: View>: UIHostingCon
203
209
}
204
210
}
205
211
206
- extension SwiftUIScreenSizingOptions {
207
- @available ( iOS 16 . 0 , * )
208
- fileprivate var uiHostingControllerSizingOptions : UIHostingControllerSizingOptions {
209
- var options = UIHostingControllerSizingOptions ( )
210
-
211
- if contains ( . preferredContentSize) {
212
- options. insert ( . preferredContentSize)
213
- }
214
-
215
- return options
216
- }
217
- }
218
-
219
212
#endif
0 commit comments