├── Examples ├── Examples │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── ExamplesApp.swift │ ├── Examples.entitlements │ └── ContentView.swift └── Examples.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── project.pbxproj ├── .gitignore ├── Package.swift ├── WrappingHStackLayout.podspec ├── LICENSE ├── README.md └── Sources └── WrappingHStack └── WrappingHStack.swift /Examples/Examples/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/Examples/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /.swiftpm 4 | /Packages 5 | /*.xcodeproj 6 | xcuserdata/ 7 | DerivedData/ 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | -------------------------------------------------------------------------------- /Examples/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Examples/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/Examples/ExamplesApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExamplesApp.swift 3 | // Examples 4 | // 5 | // Created by Konstantin Semianov on 12/2/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct ExamplesApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Examples/Examples/Examples.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.7 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "WrappingHStack", 6 | platforms: [ 7 | .iOS(.v16), 8 | .macOS(.v13), 9 | .tvOS(.v16), 10 | .watchOS(.v9), 11 | ], 12 | products: [ 13 | .library( 14 | name: "WrappingHStack", 15 | targets: ["WrappingHStack"] 16 | ), 17 | ], 18 | dependencies: [], 19 | targets: [ 20 | .target( 21 | name: "WrappingHStack", 22 | dependencies: [] 23 | ), 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /WrappingHStackLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'WrappingHStackLayout' 3 | s.version = '0.2.0' 4 | s.summary = 'WrappingHStack (FlowLayout) is a SwiftUI component similar to HStack that wraps horizontally overflowing subviews onto the next lines.' 5 | s.homepage = 'https://github.com/ksemianov/WrappingHStack' 6 | s.authors = 'Konstantin Semianov ' 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.swift_version = '5' 9 | s.ios.deployment_target = '16.0' 10 | s.osx.deployment_target = '13.0' 11 | s.source = { :git => 'https://github.com/ksemianov/WrappingHStack.git', :tag => s.version } 12 | s.source_files = 'Sources/WrappingHStack/*.swift' 13 | end 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Konstantin Semianov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Examples/Examples/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "1x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "2x", 16 | "size" : "16x16" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "1x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "2x", 26 | "size" : "32x32" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "2x", 36 | "size" : "128x128" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "1x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "2x", 46 | "size" : "256x256" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "1x", 51 | "size" : "512x512" 52 | }, 53 | { 54 | "idiom" : "mac", 55 | "scale" : "2x", 56 | "size" : "512x512" 57 | } 58 | ], 59 | "info" : { 60 | "author" : "xcode", 61 | "version" : 1 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Examples/Examples/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Examples 4 | // 5 | // Created by Konstantin Semianov on 12/2/22. 6 | // 7 | 8 | import SwiftUI 9 | import WrappingHStack 10 | 11 | struct ContentView: View { 12 | struct TagModel: Identifiable { 13 | var id = UUID() 14 | var text: String 15 | var horizontalPadding: CGFloat 16 | var verticalPadding: CGFloat 17 | } 18 | 19 | let tags = (0..<5).map { 20 | TagModel(text: "Value \($0)", 21 | horizontalPadding: CGFloat.random(in: 10...40), 22 | verticalPadding: CGFloat.random(in: 10...40)) 23 | } 24 | 25 | static let ticksPerSecond = CGFloat(60) 26 | @State private var step = 0 27 | var width: CGFloat { 28 | let period = CGFloat(10) 29 | let widthRange: Range = 250..<650 30 | let averageWidth = (widthRange.lowerBound + widthRange.upperBound) / 2 31 | let rangeWidth = widthRange.upperBound - widthRange.lowerBound 32 | let phase = cos(CGFloat(step) / Self.ticksPerSecond * 2 * .pi / period) 33 | 34 | return averageWidth + (phase - 0.5) * rangeWidth / 2 35 | } 36 | 37 | let timer = Timer.publish(every: 1 / ticksPerSecond, on: .main, in: .common).autoconnect() 38 | 39 | var body: some View { 40 | HStack { 41 | WrappingHStack(alignment: .leading) { 42 | ForEach(tags) { 43 | Text($0.text) 44 | .foregroundColor(Color.white) 45 | .padding(.horizontal, $0.horizontalPadding) 46 | .padding(.vertical, $0.verticalPadding) 47 | .background(Color.gray) 48 | .cornerRadius(16) 49 | } 50 | } 51 | .frame(width: width) 52 | .background(Rectangle().stroke(Color.accentColor)) 53 | .onReceive(timer) { _ in step += 1 } 54 | .animation(.default, value: step) 55 | .padding() 56 | 57 | Spacer(minLength: .zero) 58 | } 59 | } 60 | } 61 | 62 | struct ContentView_Previews: PreviewProvider { 63 | static var previews: some View { 64 | ContentView() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WrappingHStack 2 | 3 | A SwiftUI component similar to `HStack` that wraps horizontally overflowing subviews onto the next lines. 4 | 5 | * [Overview](#overview) 6 | * [Examples](#examples) 7 | * [Installation](#installation) 8 | * [License](#license) 9 | 10 | ## Overview 11 | 12 | The library is based on the SwiftUI's `Layout` protocol and thus has the following deployment requirement: 13 | 14 | > iOS 16.0+, iPadOS 16.0+, macOS 13.0+, Mac Catalyst 16.0+, tvOS 16.0+, watchOS 9.0+ 15 | 16 | The component's signature: 17 | 18 | ``` swift 19 | WrappingHStack(alignment: Alignment = .center, 20 | horizontalSpacing: CGFloat? = nil, 21 | verticalSpacing: CGFloat? = nil, 22 | fitContentWidth: Bool = false) 23 | ``` 24 | 25 | The component, by default, uses the `.center` alignment, but also supports the following alignment values: `.center`, `.leading`, `.topLeading`, `.top`, `.topTrailing`, `.trailing`, `.bottomTrailing`, `.bottom`, `.bottomLeading`. The rest are treated as `.center`. 26 | 27 | The component, by default, uses the system provided spacing, but it's also possible to specify explicit `horizontalSpacing` and `verticalSpacing`. 28 | 29 | The component, by default, spans the entire available width. However, you can set `fitContentWidth` to `true` to make it adjust its width based on its content. 30 | 31 | The component assumes that the largest subview fits into the bounds and there are no infinitely growing subviews. The component assumes that it can grow vertically as much as necessary to fit into the width constraint. 32 | 33 | ## Examples 34 | 35 | An array of tags: 36 | 37 | ![WrappingHStack](https://user-images.githubusercontent.com/18328341/205410620-19e66e2c-6dc4-4354-9c6a-f25a3704b2ac.gif) 38 | 39 | ``` swift 40 | WrappingHStack(alignment: .leading) { 41 | ForEach(tags) { 42 | Text($0.text) 43 | .foregroundColor(Color.white) 44 | .padding(.horizontal, $0.horizontalPadding) 45 | .padding(.vertical, $0.verticalPadding) 46 | .background(Color.gray) 47 | .cornerRadius(16) 48 | } 49 | } 50 | ``` 51 | 52 | 53 | ## Installation 54 | 55 | You can add WrappingHStack to an Xcode project by adding it as a package dependency. 56 | 57 | > https://github.com/ksemianov/WrappingHStack 58 | 59 | If you want to use WrappingHStack in a [SwiftPM](https://swift.org/package-manager/) project, 60 | it's as simple as adding it to a `dependencies` clause in your `Package.swift`: 61 | 62 | ``` swift 63 | dependencies: [ 64 | .package(url: "https://github.com/ksemianov/WrappingHStack", from: "0.2.0") 65 | ] 66 | ``` 67 | 68 | If you want to use WrappingHStack in a [CocoaPods](https://cocoapods.org) project, 69 | add this line to your `Podfile`: 70 | 71 | ``` ruby 72 | 'WrappingHStackLayout', '~> 0.2.0' 73 | ``` 74 | 75 | ## License 76 | 77 | This library is released under the MIT license. See [LICENSE](LICENSE) for details. 78 | -------------------------------------------------------------------------------- /Sources/WrappingHStack/WrappingHStack.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WrappingHStack.swift 3 | // LayoutPlayground 4 | // 5 | // Created by Konstantin Semianov on 11/30/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// A view that arranges its subviews in horizontal line and wraps them to the next lines if necessary. 11 | public struct WrappingHStack: Layout { 12 | /// The guide for aligning the subviews in this stack. This guide has the same screen coordinate for every subview. 13 | public var alignment: Alignment 14 | 15 | /// The distance between adjacent subviews in a row or `nil` if you want the stack to choose a default distance. 16 | public var horizontalSpacing: CGFloat? 17 | 18 | /// The distance between consequtive rows or`nil` if you want the stack to choose a default distance. 19 | public var verticalSpacing: CGFloat? 20 | 21 | /// Determines if the width of the stack should adjust to fit its content. 22 | /// 23 | /// If set to `true`, the stack's width will be based on its content rather than filling the available width. 24 | /// If set to `false` (default), it will occupy the full available width. 25 | public var fitContentWidth: Bool 26 | 27 | /// Creates a wrapping horizontal stack with the given spacings and alignment. 28 | /// 29 | /// - Parameters: 30 | /// - alignment: The guide for aligning the subviews in this stack. This guide has the same screen coordinate for every subview. 31 | /// - horizontalSpacing: The distance between adjacent subviews in a row or `nil` if you want the stack to choose a default distance. 32 | /// - verticalSpacing: The distance between consequtive rows or`nil` if you want the stack to choose a default distance. 33 | /// - fitContentWidth: Determines if the width of the stack should adjust to fit its content. 34 | /// - content: A view builder that creates the content of this stack. 35 | @inlinable public init(alignment: Alignment = .center, 36 | horizontalSpacing: CGFloat? = nil, 37 | verticalSpacing: CGFloat? = nil, 38 | fitContentWidth: Bool = false) { 39 | self.alignment = alignment 40 | self.horizontalSpacing = horizontalSpacing 41 | self.verticalSpacing = verticalSpacing 42 | self.fitContentWidth = fitContentWidth 43 | } 44 | 45 | public static var layoutProperties: LayoutProperties { 46 | var properties = LayoutProperties() 47 | properties.stackOrientation = .horizontal 48 | 49 | return properties 50 | } 51 | 52 | /// A shared computation between `sizeThatFits` and `placeSubviews`. 53 | public struct Cache { 54 | 55 | /// The minimal size of the view. 56 | var minSize: CGSize 57 | 58 | /// The cached rows. 59 | var rows: (Int, [Row])? 60 | } 61 | 62 | public func makeCache(subviews: Subviews) -> Cache { 63 | Cache(minSize: minSize(subviews: subviews)) 64 | } 65 | 66 | public func updateCache(_ cache: inout Cache, subviews: Subviews) { 67 | cache.minSize = minSize(subviews: subviews) 68 | } 69 | 70 | public func sizeThatFits(proposal: ProposedViewSize, 71 | subviews: Subviews, 72 | cache: inout Cache) -> CGSize { 73 | let rows = arrangeRows(proposal: proposal, subviews: subviews, cache: &cache) 74 | 75 | if rows.isEmpty { return cache.minSize } 76 | 77 | var width: CGFloat = rows.map { $0.width }.reduce(.zero) { max($0, $1) } 78 | 79 | if !fitContentWidth, let proposalWidth = proposal.width { 80 | width = max(width, proposalWidth) 81 | } 82 | 83 | var height: CGFloat = .zero 84 | if let lastRow = rows.last { 85 | height = lastRow.yOffset + lastRow.height 86 | } 87 | 88 | return CGSize(width: width, height: height) 89 | } 90 | 91 | public func placeSubviews(in bounds: CGRect, 92 | proposal: ProposedViewSize, 93 | subviews: Subviews, 94 | cache: inout Cache) { 95 | let rows = arrangeRows(proposal: proposal, subviews: subviews, cache: &cache) 96 | 97 | let anchor = UnitPoint(alignment) 98 | 99 | for row in rows { 100 | for element in row.elements { 101 | let x: CGFloat = element.xOffset + anchor.x * (bounds.width - row.width) 102 | let y: CGFloat = row.yOffset + anchor.y * (row.height - element.size.height) 103 | let point = CGPoint(x: x + bounds.minX, y: y + bounds.minY) 104 | 105 | subviews[element.index].place(at: point, anchor: .topLeading, proposal: proposal) 106 | } 107 | } 108 | } 109 | } 110 | 111 | extension WrappingHStack { 112 | struct Row { 113 | var elements: [(index: Int, size: CGSize, xOffset: CGFloat)] = [] 114 | var yOffset: CGFloat = .zero 115 | var width: CGFloat = .zero 116 | var height: CGFloat = .zero 117 | } 118 | 119 | private func arrangeRows(proposal: ProposedViewSize, 120 | subviews: Subviews, 121 | cache: inout Cache) -> [Row] { 122 | if subviews.isEmpty { 123 | return [] 124 | } 125 | 126 | if cache.minSize.width > proposal.width ?? .infinity, 127 | cache.minSize.height > proposal.height ?? .infinity { 128 | return [] 129 | } 130 | 131 | let sizes = subviews.map { $0.sizeThatFits(proposal) } 132 | 133 | let hash = computeHash(proposal: proposal, sizes: sizes) 134 | if let (oldHash, oldRows) = cache.rows, 135 | oldHash == hash { 136 | return oldRows 137 | } 138 | 139 | var currentX = CGFloat.zero 140 | var currentRow = Row() 141 | var rows = [Row]() 142 | 143 | for index in subviews.indices { 144 | var spacing = CGFloat.zero 145 | if let previousIndex = currentRow.elements.last?.index { 146 | spacing = horizontalSpacing(subviews[previousIndex], subviews[index]) 147 | } 148 | 149 | let size = sizes[index] 150 | 151 | if currentX + size.width + spacing > proposal.width ?? .infinity, 152 | !currentRow.elements.isEmpty { 153 | currentRow.width = currentX 154 | rows.append(currentRow) 155 | currentRow = Row() 156 | spacing = .zero 157 | currentX = .zero 158 | } 159 | 160 | currentRow.elements.append((index, sizes[index], currentX + spacing)) 161 | currentX += size.width + spacing 162 | } 163 | 164 | if !currentRow.elements.isEmpty { 165 | currentRow.width = currentX 166 | rows.append(currentRow) 167 | } 168 | 169 | var currentY = CGFloat.zero 170 | var previousMaxHeightIndex: Int? 171 | 172 | for index in rows.indices { 173 | let maxHeightIndex = rows[index].elements 174 | .max { $0.size.height < $1.size.height }! 175 | .index 176 | 177 | let size = sizes[maxHeightIndex] 178 | 179 | var spacing = CGFloat.zero 180 | if let previousMaxHeightIndex { 181 | spacing = verticalSpacing(subviews[previousMaxHeightIndex], subviews[maxHeightIndex]) 182 | } 183 | 184 | rows[index].yOffset = currentY + spacing 185 | currentY += size.height + spacing 186 | rows[index].height = size.height 187 | previousMaxHeightIndex = maxHeightIndex 188 | } 189 | 190 | cache.rows = (hash, rows) 191 | 192 | return rows 193 | } 194 | 195 | private func computeHash(proposal: ProposedViewSize, sizes: [CGSize]) -> Int { 196 | let proposal = proposal.replacingUnspecifiedDimensions(by: .infinity) 197 | 198 | var hasher = Hasher() 199 | 200 | for size in [proposal] + sizes { 201 | hasher.combine(size.width) 202 | hasher.combine(size.height) 203 | } 204 | 205 | return hasher.finalize() 206 | } 207 | 208 | private func minSize(subviews: Subviews) -> CGSize { 209 | subviews 210 | .map { $0.sizeThatFits(.zero) } 211 | .reduce(CGSize.zero) { CGSize(width: max($0.width, $1.width), height: max($0.height, $1.height)) } 212 | } 213 | 214 | private func horizontalSpacing(_ lhs: LayoutSubview, _ rhs: LayoutSubview) -> CGFloat { 215 | if let horizontalSpacing { return horizontalSpacing } 216 | 217 | return lhs.spacing.distance(to: rhs.spacing, along: .horizontal) 218 | } 219 | 220 | private func verticalSpacing(_ lhs: LayoutSubview, _ rhs: LayoutSubview) -> CGFloat { 221 | if let verticalSpacing { return verticalSpacing } 222 | 223 | return lhs.spacing.distance(to: rhs.spacing, along: .vertical) 224 | } 225 | } 226 | 227 | private extension CGSize { 228 | static var infinity: Self { 229 | .init(width: CGFloat.infinity, height: CGFloat.infinity) 230 | } 231 | } 232 | 233 | private extension UnitPoint { 234 | init(_ alignment: Alignment) { 235 | switch alignment { 236 | case .leading: 237 | self = .leading 238 | case .topLeading: 239 | self = .topLeading 240 | case .top: 241 | self = .top 242 | case .topTrailing: 243 | self = .topTrailing 244 | case .trailing: 245 | self = .trailing 246 | case .bottomTrailing: 247 | self = .bottomTrailing 248 | case .bottom: 249 | self = .bottom 250 | case .bottomLeading: 251 | self = .bottomLeading 252 | default: 253 | self = .center 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /Examples/Examples.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4836B22429399DCD0054A196 /* ExamplesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4836B22329399DCD0054A196 /* ExamplesApp.swift */; }; 11 | 4836B22629399DCD0054A196 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4836B22529399DCD0054A196 /* ContentView.swift */; }; 12 | 4836B22829399DCE0054A196 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4836B22729399DCE0054A196 /* Assets.xcassets */; }; 13 | 4836B22C29399DCE0054A196 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4836B22B29399DCE0054A196 /* Preview Assets.xcassets */; }; 14 | 484393332942E5020036119C /* WrappingHStack in Frameworks */ = {isa = PBXBuildFile; productRef = 484393322942E5020036119C /* WrappingHStack */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 4836B22029399DCD0054A196 /* Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 4836B22329399DCD0054A196 /* ExamplesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesApp.swift; sourceTree = ""; }; 20 | 4836B22529399DCD0054A196 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 21 | 4836B22729399DCE0054A196 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 22 | 4836B22929399DCE0054A196 /* Examples.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Examples.entitlements; sourceTree = ""; }; 23 | 4836B22B29399DCE0054A196 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 24 | 484393312942E4CD0036119C /* WrappingHStack */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = WrappingHStack; path = ..; sourceTree = ""; }; 25 | /* End PBXFileReference section */ 26 | 27 | /* Begin PBXFrameworksBuildPhase section */ 28 | 4836B21D29399DCD0054A196 /* Frameworks */ = { 29 | isa = PBXFrameworksBuildPhase; 30 | buildActionMask = 2147483647; 31 | files = ( 32 | 484393332942E5020036119C /* WrappingHStack in Frameworks */, 33 | ); 34 | runOnlyForDeploymentPostprocessing = 0; 35 | }; 36 | /* End PBXFrameworksBuildPhase section */ 37 | 38 | /* Begin PBXGroup section */ 39 | 4836B21729399DCD0054A196 = { 40 | isa = PBXGroup; 41 | children = ( 42 | 4836B22229399DCD0054A196 /* Examples */, 43 | 4836B22129399DCD0054A196 /* Products */, 44 | 48FC27C829399EAE0035349A /* Frameworks */, 45 | 484393302942E4CD0036119C /* Packages */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 4836B22129399DCD0054A196 /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 4836B22029399DCD0054A196 /* Examples.app */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | 4836B22229399DCD0054A196 /* Examples */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 4836B22329399DCD0054A196 /* ExamplesApp.swift */, 61 | 4836B22529399DCD0054A196 /* ContentView.swift */, 62 | 4836B22729399DCE0054A196 /* Assets.xcassets */, 63 | 4836B22929399DCE0054A196 /* Examples.entitlements */, 64 | 4836B22A29399DCE0054A196 /* Preview Content */, 65 | ); 66 | path = Examples; 67 | sourceTree = ""; 68 | }; 69 | 4836B22A29399DCE0054A196 /* Preview Content */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 4836B22B29399DCE0054A196 /* Preview Assets.xcassets */, 73 | ); 74 | path = "Preview Content"; 75 | sourceTree = ""; 76 | }; 77 | 484393302942E4CD0036119C /* Packages */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 484393312942E4CD0036119C /* WrappingHStack */, 81 | ); 82 | name = Packages; 83 | sourceTree = ""; 84 | }; 85 | 48FC27C829399EAE0035349A /* Frameworks */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | ); 89 | name = Frameworks; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXNativeTarget section */ 95 | 4836B21F29399DCD0054A196 /* Examples */ = { 96 | isa = PBXNativeTarget; 97 | buildConfigurationList = 4836B22F29399DCE0054A196 /* Build configuration list for PBXNativeTarget "Examples" */; 98 | buildPhases = ( 99 | 4836B21C29399DCD0054A196 /* Sources */, 100 | 4836B21D29399DCD0054A196 /* Frameworks */, 101 | 4836B21E29399DCD0054A196 /* Resources */, 102 | ); 103 | buildRules = ( 104 | ); 105 | dependencies = ( 106 | ); 107 | name = Examples; 108 | packageProductDependencies = ( 109 | 484393322942E5020036119C /* WrappingHStack */, 110 | ); 111 | productName = Examples; 112 | productReference = 4836B22029399DCD0054A196 /* Examples.app */; 113 | productType = "com.apple.product-type.application"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | 4836B21829399DCD0054A196 /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | BuildIndependentTargetsInParallel = 1; 122 | LastSwiftUpdateCheck = 1410; 123 | LastUpgradeCheck = 1410; 124 | TargetAttributes = { 125 | 4836B21F29399DCD0054A196 = { 126 | CreatedOnToolsVersion = 14.1; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 4836B21B29399DCD0054A196 /* Build configuration list for PBXProject "Examples" */; 131 | compatibilityVersion = "Xcode 14.0"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 4836B21729399DCD0054A196; 139 | productRefGroup = 4836B22129399DCD0054A196 /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 4836B21F29399DCD0054A196 /* Examples */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 4836B21E29399DCD0054A196 /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 4836B22C29399DCE0054A196 /* Preview Assets.xcassets in Resources */, 154 | 4836B22829399DCE0054A196 /* Assets.xcassets in Resources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXResourcesBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | 4836B21C29399DCD0054A196 /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 4836B22629399DCD0054A196 /* ContentView.swift in Sources */, 166 | 4836B22429399DCD0054A196 /* ExamplesApp.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin XCBuildConfiguration section */ 173 | 4836B22D29399DCE0054A196 /* Debug */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | ALWAYS_SEARCH_USER_PATHS = NO; 177 | CLANG_ANALYZER_NONNULL = YES; 178 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 179 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 180 | CLANG_ENABLE_MODULES = YES; 181 | CLANG_ENABLE_OBJC_ARC = YES; 182 | CLANG_ENABLE_OBJC_WEAK = YES; 183 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 184 | CLANG_WARN_BOOL_CONVERSION = YES; 185 | CLANG_WARN_COMMA = YES; 186 | CLANG_WARN_CONSTANT_CONVERSION = YES; 187 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 189 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 190 | CLANG_WARN_EMPTY_BODY = YES; 191 | CLANG_WARN_ENUM_CONVERSION = YES; 192 | CLANG_WARN_INFINITE_RECURSION = YES; 193 | CLANG_WARN_INT_CONVERSION = YES; 194 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 195 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 196 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 199 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 200 | CLANG_WARN_STRICT_PROTOTYPES = YES; 201 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 202 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | COPY_PHASE_STRIP = NO; 206 | DEBUG_INFORMATION_FORMAT = dwarf; 207 | ENABLE_STRICT_OBJC_MSGSEND = YES; 208 | ENABLE_TESTABILITY = YES; 209 | GCC_C_LANGUAGE_STANDARD = gnu11; 210 | GCC_DYNAMIC_NO_PIC = NO; 211 | GCC_NO_COMMON_BLOCKS = YES; 212 | GCC_OPTIMIZATION_LEVEL = 0; 213 | GCC_PREPROCESSOR_DEFINITIONS = ( 214 | "DEBUG=1", 215 | "$(inherited)", 216 | ); 217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 219 | GCC_WARN_UNDECLARED_SELECTOR = YES; 220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 221 | GCC_WARN_UNUSED_FUNCTION = YES; 222 | GCC_WARN_UNUSED_VARIABLE = YES; 223 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 224 | MTL_FAST_MATH = YES; 225 | ONLY_ACTIVE_ARCH = YES; 226 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 227 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 228 | }; 229 | name = Debug; 230 | }; 231 | 4836B22E29399DCE0054A196 /* Release */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | ALWAYS_SEARCH_USER_PATHS = NO; 235 | CLANG_ANALYZER_NONNULL = YES; 236 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 237 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 238 | CLANG_ENABLE_MODULES = YES; 239 | CLANG_ENABLE_OBJC_ARC = YES; 240 | CLANG_ENABLE_OBJC_WEAK = YES; 241 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 242 | CLANG_WARN_BOOL_CONVERSION = YES; 243 | CLANG_WARN_COMMA = YES; 244 | CLANG_WARN_CONSTANT_CONVERSION = YES; 245 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 246 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 247 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 248 | CLANG_WARN_EMPTY_BODY = YES; 249 | CLANG_WARN_ENUM_CONVERSION = YES; 250 | CLANG_WARN_INFINITE_RECURSION = YES; 251 | CLANG_WARN_INT_CONVERSION = YES; 252 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 253 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 254 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 256 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu11; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | MTL_ENABLE_DEBUG_INFO = NO; 276 | MTL_FAST_MATH = YES; 277 | SWIFT_COMPILATION_MODE = wholemodule; 278 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 279 | }; 280 | name = Release; 281 | }; 282 | 4836B23029399DCE0054A196 /* Debug */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 286 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 287 | CODE_SIGN_ENTITLEMENTS = Examples/Examples.entitlements; 288 | CODE_SIGN_STYLE = Automatic; 289 | CURRENT_PROJECT_VERSION = 1; 290 | DEVELOPMENT_ASSET_PATHS = "\"Examples/Preview Content\""; 291 | DEVELOPMENT_TEAM = X3YFK565V9; 292 | ENABLE_HARDENED_RUNTIME = YES; 293 | ENABLE_PREVIEWS = YES; 294 | GENERATE_INFOPLIST_FILE = YES; 295 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; 296 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; 297 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; 298 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; 299 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; 300 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; 301 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; 302 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; 303 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 304 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 305 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 306 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; 307 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; 308 | MACOSX_DEPLOYMENT_TARGET = 13.0; 309 | MARKETING_VERSION = 1.0; 310 | PRODUCT_BUNDLE_IDENTIFIER = com.ksemianov.Examples; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | SDKROOT = auto; 313 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; 314 | SWIFT_EMIT_LOC_STRINGS = YES; 315 | SWIFT_VERSION = 5.0; 316 | TARGETED_DEVICE_FAMILY = "1,2"; 317 | }; 318 | name = Debug; 319 | }; 320 | 4836B23129399DCE0054A196 /* Release */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 325 | CODE_SIGN_ENTITLEMENTS = Examples/Examples.entitlements; 326 | CODE_SIGN_STYLE = Automatic; 327 | CURRENT_PROJECT_VERSION = 1; 328 | DEVELOPMENT_ASSET_PATHS = "\"Examples/Preview Content\""; 329 | DEVELOPMENT_TEAM = X3YFK565V9; 330 | ENABLE_HARDENED_RUNTIME = YES; 331 | ENABLE_PREVIEWS = YES; 332 | GENERATE_INFOPLIST_FILE = YES; 333 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; 334 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; 335 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; 336 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; 337 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; 338 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; 339 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; 340 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; 341 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 342 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 343 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 344 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; 345 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; 346 | MACOSX_DEPLOYMENT_TARGET = 13.0; 347 | MARKETING_VERSION = 1.0; 348 | PRODUCT_BUNDLE_IDENTIFIER = com.ksemianov.Examples; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | SDKROOT = auto; 351 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; 352 | SWIFT_EMIT_LOC_STRINGS = YES; 353 | SWIFT_VERSION = 5.0; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | }; 356 | name = Release; 357 | }; 358 | /* End XCBuildConfiguration section */ 359 | 360 | /* Begin XCConfigurationList section */ 361 | 4836B21B29399DCD0054A196 /* Build configuration list for PBXProject "Examples" */ = { 362 | isa = XCConfigurationList; 363 | buildConfigurations = ( 364 | 4836B22D29399DCE0054A196 /* Debug */, 365 | 4836B22E29399DCE0054A196 /* Release */, 366 | ); 367 | defaultConfigurationIsVisible = 0; 368 | defaultConfigurationName = Release; 369 | }; 370 | 4836B22F29399DCE0054A196 /* Build configuration list for PBXNativeTarget "Examples" */ = { 371 | isa = XCConfigurationList; 372 | buildConfigurations = ( 373 | 4836B23029399DCE0054A196 /* Debug */, 374 | 4836B23129399DCE0054A196 /* Release */, 375 | ); 376 | defaultConfigurationIsVisible = 0; 377 | defaultConfigurationName = Release; 378 | }; 379 | /* End XCConfigurationList section */ 380 | 381 | /* Begin XCSwiftPackageProductDependency section */ 382 | 484393322942E5020036119C /* WrappingHStack */ = { 383 | isa = XCSwiftPackageProductDependency; 384 | productName = WrappingHStack; 385 | }; 386 | /* End XCSwiftPackageProductDependency section */ 387 | }; 388 | rootObject = 4836B21829399DCD0054A196 /* Project object */; 389 | } 390 | --------------------------------------------------------------------------------