├── 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 | 
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 |
--------------------------------------------------------------------------------