@@ -28,22 +28,32 @@ where Content == Never {
2828 /// the view hierarchy (i.e. through bindings), and is often the view's delegate.
2929 func makeCoordinator( ) -> Coordinator
3030
31- /// Compute the view's preferred size.
31+ /// Compute the view's size.
3232 /// - Parameters:
3333 /// - proposal: The proposed frame for the view to render in.
3434 /// - uiVIew: The view being queried for its preferred size.
3535 /// - context: The context, including the coordinator and environment values.
36- /// - Returns: The preferred size for the view, ideally one that fits within `proposal`,
37- /// or `nil` to use a default size for this view.
38- func sizeThatFits(
39- _ proposal: CGSize , uiView: UIViewType , context: UIViewRepresentableContext < Coordinator >
40- ) -> CGSize ?
36+ /// - Returns: Information about the view's size. The ``SwiftCrossUI/ViewSize/size``
37+ /// property is what frame the view will actually be rendered with if the current layout
38+ /// pass is not a dry run, while the other properties are used to inform the layout engine
39+ /// how big or small the view can be. Pass `nil` for the maximum width/height if the view
40+ /// has no maximum size (and therefore may occupy the entire screen).
41+ ///
42+ /// The default implementation uses `uiView.intrinsicContentSize` and `uiView.sizeThatFits(_:)`
43+ /// to determine the return value.
44+ func determineViewSize(
45+ for proposal: SIMD2 < Int > , uiView: UIViewType ,
46+ context: UIViewRepresentableContext < Coordinator >
47+ ) -> ViewSize
4148
4249 /// Called to clean up the view when it's removed.
4350 /// - Parameters:
44- /// - uiVIew: The view being queried for its preferred size .
51+ /// - uiVIew: The view being dismantled .
4552 /// - coordinator: The coordinator.
4653 ///
54+ /// This method is called after all UIKit lifecycle methods, such as
55+ /// `uiView.didMoveToSuperview()`.
56+ ///
4757 /// The default implementation does nothing.
4858 static func dismantleUIView( _ uiView: UIViewType , coordinator: Coordinator )
4959}
@@ -53,17 +63,30 @@ extension UIViewRepresentable {
5363 // no-op
5464 }
5565
56- public func sizeThatFits(
57- _ proposal: CGSize , uiView: UIViewType , context _: UIViewRepresentableContext < Coordinator >
58- ) -> CGSize ? {
59- // For many growable views, such as WKWebView, sizeThatFits just returns the current
60- // size -- which is 0 x 0 on first render. So, check if the view can grow to fill
61- // the available space first.
62- let intrinsicContentSize = uiView. intrinsicContentSize
63- if intrinsicContentSize. width < 0.0 || intrinsicContentSize. height < 0.0 {
64- return nil
65- }
66- return uiView. sizeThatFits ( proposal)
66+ public func determineViewSize(
67+ for proposal: SIMD2 < Int > , uiView: UIViewType ,
68+ context _: UIViewRepresentableContext < Coordinator >
69+ ) -> ViewSize {
70+ let intrinsicSize = uiView. intrinsicContentSize
71+ let sizeThatFits = uiView. sizeThatFits (
72+ CGSize ( width: CGFloat ( proposal. x) , height: CGFloat ( proposal. y) ) )
73+ let roundedSize = SIMD2 (
74+ Int ( sizeThatFits. width. rounded ( . up) ) ,
75+ Int ( sizeThatFits. height. rounded ( . up) ) )
76+ return ViewSize (
77+ size: SIMD2 (
78+ intrinsicSize. width < 0.0 ? proposal. x : min ( proposal. x, roundedSize. x) ,
79+ intrinsicSize. height < 0.0 ? proposal. y : min ( proposal. y, roundedSize. y)
80+ ) ,
81+ idealSize: SIMD2 (
82+ intrinsicSize. width < 0.0 ? proposal. x : roundedSize. x,
83+ intrinsicSize. height < 0.0 ? proposal. y : roundedSize. y
84+ ) ,
85+ minimumWidth: max ( 0 , Int ( intrinsicSize. width. rounded ( . awayFromZero) ) ) ,
86+ minimumHeight: max ( 0 , Int ( intrinsicSize. height. rounded ( . awayFromZero) ) ) ,
87+ maximumWidth: nil ,
88+ maximumHeight: nil
89+ )
6790 }
6891}
6992
@@ -108,34 +131,14 @@ where Self: UIViewRepresentable {
108131 dryRun: Bool
109132 ) -> ViewUpdateResult {
110133 let representingWidget = widget as! RepresentingWidget < Self >
111- representingWidget. updateContext ( environment : environment)
134+ representingWidget. update ( with : environment)
112135
113- let preferredSize =
114- representingWidget. representable. sizeThatFits (
115- CGSize ( width : proposedSize. x , height : proposedSize . y ) ,
136+ let size =
137+ representingWidget. representable. determineViewSize (
138+ for : proposedSize,
116139 uiView: representingWidget. subview,
117140 context: representingWidget. context!
118- ) ?? representingWidget. subview. intrinsicContentSize
119-
120- let roundedSize = SIMD2 (
121- Int ( preferredSize. width. rounded ( . awayFromZero) ) ,
122- Int ( preferredSize. height. rounded ( . awayFromZero) ) )
123-
124- // Not only does -1 x -1 mean "grow to fill", UIKit allows you to return -1 for only one axis!
125- let size = ViewSize (
126- size: SIMD2 (
127- roundedSize. x < 0 ? proposedSize. x : min ( proposedSize. x, roundedSize. x) ,
128- roundedSize. y < 0 ? proposedSize. y : min ( proposedSize. y, roundedSize. y)
129- ) ,
130- idealSize: SIMD2 (
131- roundedSize. x < 0 ? proposedSize. x : roundedSize. x,
132- roundedSize. y < 0 ? proposedSize. y : roundedSize. y
133- ) ,
134- minimumWidth: roundedSize. x > proposedSize. x ? roundedSize. x : 0 ,
135- minimumHeight: roundedSize. y > proposedSize. y ? roundedSize. y : 0 ,
136- maximumWidth: nil ,
137- maximumHeight: nil
138- )
141+ )
139142
140143 if !dryRun {
141144 representingWidget. width = size. size. x
@@ -173,7 +176,7 @@ final class RepresentingWidget<Representable: UIViewRepresentable>: BaseWidget {
173176 return view
174177 } ( )
175178
176- func updateContext ( environment: EnvironmentValues ) {
179+ func update ( with environment: EnvironmentValues ) {
177180 if context == nil {
178181 context = . init( coordinator: representable. makeCoordinator ( ) , environment: environment)
179182 } else {
0 commit comments