OptimalSize.swift (2853B)
1 // MIT License 2 // by Benzy Neez 3 // https://stackoverflow.com/questions/77631146/how-to-make-scrollview-shrink-to-fit-and-take-minimum-space-without-hacks 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 // and associated documentation files (the "Software"), to deal in the Software without restriction, 7 // including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in all copies or 12 // substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 // 20 /** 21 * @author Marc Stibane 22 */ 23 import SwiftUI 24 25 @available(iOS 16.0, *) 26 struct OptimalSize: Layout { 27 let axes: Axis.Set 28 29 /// Computes the optimal size for a ScrollView (which would grab all it could get otherwise) 30 /// - Parameters: 31 /// - axes: Axes this content must fit in. 32 init(_ axes: Axis.Set = [.horizontal, .vertical]) { 33 self.axes = axes 34 } 35 36 public func sizeThatFits( 37 proposal: ProposedViewSize, 38 subviews: Subviews, 39 cache: inout () 40 ) -> CGSize { 41 let result: CGSize 42 let horz = axes.contains(.horizontal) 43 let vert = axes.contains(.vertical) 44 45 if let firstSubview = subviews.first { 46 let containerWidth = proposal.width ?? .infinity 47 let containerHeight = proposal.height ?? .infinity 48 let size = firstSubview.sizeThatFits(.init(width: vert ? containerWidth : nil, 49 height: horz ? containerHeight : nil)) 50 result = CGSize(width: min(size.width, containerWidth), 51 height: min(size.height, containerHeight)) 52 } else { 53 result = .zero 54 } 55 return result 56 } 57 58 public func placeSubviews( 59 in bounds: CGRect, 60 proposal: ProposedViewSize, 61 subviews: Subviews, 62 cache: inout () 63 ) { 64 if let firstSubview = subviews.first { 65 firstSubview.place( 66 at: CGPoint(x: bounds.minX, y: bounds.minY), 67 proposal: .init(width: bounds.width, height: bounds.height) 68 ) 69 } 70 } 71 }