├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Package.swift
├── README.md
├── Sources
└── EquallySpacedStack
│ └── EquallySpacedStack.swift
├── Tests
├── EquallySpacedStackTests
│ ├── EquallySpacedStackTests.swift
│ └── XCTestManifests.swift
└── LinuxMain.swift
└── distributed_stack-aaae1c88c4412c44c53045f94508be1c-103f7.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | .swiftpm/xcode/xcuserdata
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "EquallySpacedStack",
8 | platforms: [
9 | .iOS(.v13)
10 | ],
11 | products: [
12 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
13 | .library(
14 | name: "EquallySpacedStack",
15 | targets: ["EquallySpacedStack"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
24 | .target(
25 | name: "EquallySpacedStack",
26 | dependencies: []),
27 | .testTarget(
28 | name: "EquallySpacedStackTests",
29 | dependencies: ["EquallySpacedStack"]),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EquallySpacedStack
2 |
3 | This package provides two new Views: `EquallySpacedHStack` and `EquallySpacedVStack`. They behave like their respective Stacks but distribute the subviews by adding equal space between them.
4 |
5 | ## Status
6 |
7 | This package is work in progress. I'm still trying to figure out the best way of doing custom layouts in SwiftUI.
8 |
9 | If you want to follow the process you can read my blog or [follow me](https://twitter.com/alexito4) on Twitter:
10 | - [Trying to understand custom layouts in SwiftUI](https://alejandromp.com/blog/2019/06/12/custom-layouts-in-swiftui/)
11 | - [SwiftUI TupleView and equally spaced stacks](https://alejandromp.com/blog/2019/06/13/implementing-a-equally-spaced-stack-in-swiftui-thanks-to-tupleview/)
12 |
13 | ## Usage
14 |
15 | Just replace `HStack` or `VStack` with `EquallySpacedHStack` or `EquallySpacedVStack`:
16 |
17 | ```
18 | EquallySpacedHStack {
19 | Color.red.frame(width: 50, height: 50)
20 | Color.blue.frame(width: 50, height: 50)
21 | Color.green.frame(width: 50, height: 50)
22 | }
23 | ```
24 | you can also use `ForEach`:
25 |
26 | ```
27 | EquallySpacedHStack {
28 | ForEach(1...4) { _ in
29 | Color.red.frame(width: 50, height: 50)
30 | }
31 | }
32 | ```
33 |
34 | 
35 |
36 | ## WIP
37 |
38 | - [ ] Avoid issue when using more than the supported number of nested subviews.
39 | - [ ] Support more nested subviews (max is now 4)
40 | - [ ] Avoid using the type erased `AnyView`
41 |
42 | ## Contributions & support
43 |
44 | This project is developed completely in the open, and your contributions are more than welcome.
45 |
46 | This project does not come with GitHub Issues-based support, and users are instead encouraged to become active participants in its continued development — by fixing any bugs that they encounter, or improving the documentation wherever it’s found to be lacking.
47 |
48 | If you wish to make a change, [open a Pull Request](https://github.com/alexito4/EquallySpacedStack/pull/new) — even if it just contains a draft of the changes you’re planning, or a test that reproduces an issue — and we can discuss it further from there.
49 |
50 | ## Author
51 |
52 | Alejandro Martinez | http://alejandromp.com | [@alexito4](https://twitter.com/alexito4)
53 |
--------------------------------------------------------------------------------
/Sources/EquallySpacedStack/EquallySpacedStack.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | public struct EquallySpacedHStack: View {
4 | let items: [AnyView]
5 |
6 | public init(@ViewBuilder content: () -> A) { // this init will be used for any non-supported number of TupleView
7 | let views = content()
8 | self.items = [AnyView(views)]
9 | }
10 |
11 | // MARK: TupleView support
12 |
13 | public init(@ViewBuilder content: () -> TupleView<(A, B)>) {
14 | let views = content().value
15 | self.items = [AnyView(views.0), AnyView(views.1)]
16 | }
17 |
18 | public init(@ViewBuilder content: () -> TupleView<(A, B, C)>) {
19 | let views = content().value
20 | self.items = [AnyView(views.0), AnyView(views.1), AnyView(views.2)]
21 | }
22 |
23 | public init(@ViewBuilder content: () -> TupleView<(A, B, C, D)>) {
24 | let views = content().value
25 | self.items = [AnyView(views.0), AnyView(views.1), AnyView(views.2), AnyView(views.3)]
26 | }
27 |
28 | // MARK: ForEach support
29 |
30 | public init(@ViewBuilder content: () -> ForEach) {
31 | let views = content()
32 | self.items = views.data.map({ AnyView(views.content($0)) })
33 | }
34 |
35 | public var body: some View {
36 | HStack {
37 | Spacer()
38 | ForEach(0..(@ViewBuilder content: () -> A) { // this init will be used for any non-supported number of TupleView
50 | self.items = [AnyView(content())]
51 | }
52 |
53 | // MARK: TupleView support
54 |
55 | public init(@ViewBuilder content: () -> TupleView<(A, B)>) {
56 | let views = content().value
57 | self.items = [AnyView(views.0), AnyView(views.1)]
58 | }
59 |
60 | public init(@ViewBuilder content: () -> TupleView<(A, B, C)>) {
61 | let views = content().value
62 | self.items = [AnyView(views.0), AnyView(views.1), AnyView(views.2)]
63 | }
64 |
65 | public init(@ViewBuilder content: () -> TupleView<(A, B, C, D)>) {
66 | let views = content().value
67 | self.items = [AnyView(views.0), AnyView(views.1), AnyView(views.2), AnyView(views.3)]
68 | }
69 |
70 | // MARK: ForEach support
71 |
72 | public init(@ViewBuilder content: () -> ForEach) {
73 | let views = content()
74 | self.items = views.data.map({ AnyView(views.content($0)) })
75 | }
76 |
77 | public var body: some View {
78 | VStack {
79 | Spacer()
80 | ForEach(0.. [XCTestCaseEntry] {
5 | return [
6 | testCase(EquallySpacedStackTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import EquallySpacedStackTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += EquallySpacedStackTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/distributed_stack-aaae1c88c4412c44c53045f94508be1c-103f7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexito4/EquallySpacedStack/4de61d935c784e549425c79341915ee2eda78107/distributed_stack-aaae1c88c4412c44c53045f94508be1c-103f7.png
--------------------------------------------------------------------------------