Skip to content
This repository was archived by the owner on Dec 27, 2020. It is now read-only.

Commit 79a3be0

Browse files
authored
Merge pull request #9 from spacenation/function-builder
View Builder
2 parents 86b5bed + 0f343aa commit 79a3be0

File tree

6 files changed

+86
-28
lines changed

6 files changed

+86
-28
lines changed

Examples/GridExamples iOS/ContentView.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ struct ContentView: View {
2929
}
3030
}
3131
.tag(2)
32+
BuilderLayoutView()
33+
.tabItem {
34+
VStack {
35+
Image(systemName: "rectangle.3.offgrid.fill")
36+
Text("Builder")
37+
}
38+
}
39+
.tag(3)
3240
}
3341
.accentColor(.purple)
3442
}

Examples/GridExamples macOS/ContentView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ struct ContentView: View {
2121
Text("Performance")
2222
}
2323
.tag(2)
24+
BuilderLayoutView()
25+
.tabItem {
26+
Text("Builder")
27+
}
28+
.tag(2)
2429
}
2530
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: .infinity)
2631
.padding()

Examples/GridExamples.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
FA6ED893231090FF00651DD9 /* BuilderLayoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6ED892231090FF00651DD9 /* BuilderLayoutView.swift */; };
11+
FA6ED894231090FF00651DD9 /* BuilderLayoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6ED892231090FF00651DD9 /* BuilderLayoutView.swift */; };
1012
FA902115230B32EA00BF9341 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA902114230B32EA00BF9341 /* AppDelegate.swift */; };
1113
FA902117230B32EA00BF9341 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA902116230B32EA00BF9341 /* SceneDelegate.swift */; };
1214
FA902119230B32EA00BF9341 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA902118230B32EA00BF9341 /* ContentView.swift */; };
@@ -33,6 +35,7 @@
3335
/* End PBXBuildFile section */
3436

3537
/* Begin PBXFileReference section */
38+
FA6ED892231090FF00651DD9 /* BuilderLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuilderLayoutView.swift; sourceTree = "<group>"; };
3639
FA902111230B32EA00BF9341 /* GridExamples iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GridExamples iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
3740
FA902114230B32EA00BF9341 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
3841
FA902116230B32EA00BF9341 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -157,6 +160,7 @@
157160
FA902169230B39C800BF9341 /* AutoColumnLayoutView.swift */,
158161
FA902166230B398200BF9341 /* OneColumnLayoutView.swift */,
159162
FA90216C230B3EC100BF9341 /* PerformanceLayoutView.swift */,
163+
FA6ED892231090FF00651DD9 /* BuilderLayoutView.swift */,
160164
FA902160230B379D00BF9341 /* Color+Random.swift */,
161165
FA902163230B389900BF9341 /* Card.swift */,
162166
);
@@ -276,6 +280,7 @@
276280
FA902115230B32EA00BF9341 /* AppDelegate.swift in Sources */,
277281
FA90216D230B3EC100BF9341 /* PerformanceLayoutView.swift in Sources */,
278282
FA902164230B389900BF9341 /* Card.swift in Sources */,
283+
FA6ED893231090FF00651DD9 /* BuilderLayoutView.swift in Sources */,
279284
FA902117230B32EA00BF9341 /* SceneDelegate.swift in Sources */,
280285
FA902167230B398200BF9341 /* OneColumnLayoutView.swift in Sources */,
281286
FA902119230B32EA00BF9341 /* ContentView.swift in Sources */,
@@ -290,6 +295,7 @@
290295
files = (
291296
FA902162230B379D00BF9341 /* Color+Random.swift in Sources */,
292297
FA90214B230B341C00BF9341 /* ContentView.swift in Sources */,
298+
FA6ED894231090FF00651DD9 /* BuilderLayoutView.swift in Sources */,
293299
FA90216E230B3EC100BF9341 /* PerformanceLayoutView.swift in Sources */,
294300
FA902165230B389900BF9341 /* Card.swift in Sources */,
295301
FA902149230B341C00BF9341 /* AppDelegate.swift in Sources */,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import SwiftUI
2+
import Grid
3+
4+
struct BuilderLayoutView: View {
5+
var body: some View {
6+
VStack(alignment: .center) {
7+
Section(header: Text("Section 1")) {
8+
Grid {
9+
ForEach(0...10, id: \.self) { _ in
10+
Rectangle()
11+
.foregroundColor(.random)
12+
.frame(height: 20)
13+
}
14+
}
15+
}
16+
17+
Section(header: Text("Section 2")) {
18+
Grid {
19+
ForEach(0...10, id: \.self) { _ in
20+
Rectangle()
21+
.foregroundColor(.random)
22+
.frame(height: 20)
23+
}
24+
}
25+
}
26+
}
27+
}
28+
}
29+
30+
struct BuilderLayoutView_Previews: PreviewProvider {
31+
static var previews: some View {
32+
BuilderLayoutView()
33+
}
34+
}

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ Grid(0...100, minimumItemWidth: 100) { _ in
2020
}
2121
```
2222

23+
```swift
24+
Grid {
25+
ForEach(0...10, id: \.self) { _ in
26+
Rectangle()
27+
.foregroundColor(.random)
28+
.frame(height: 20)
29+
}
30+
}
31+
```
32+
2333
```swift
2434
/// Grid with minimum item width and fixed item height.
2535

Sources/Grid/Grid.swift

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,44 @@ import SwiftUI
22

33
/// A view that arranges its children in a grid.
44
@available(iOS 13.0, OSX 10.15, *)
5-
public struct Grid<Data, Content> : View where Data: RandomAccessCollection, Content : View {
6-
private let data: [Data.Element]
5+
public struct Grid<Content> : View where Content : View {
6+
private let items: [AnyView]
77
private let minimumItemWidth: CGFloat
88
private let spacing: CGFloat
9-
private let content: (Data.Element) -> Content
9+
10+
public init<Data: RandomAccessCollection>(_ data: Data, minimumItemWidth: CGFloat = 300, spacing: CGFloat = 8, @ViewBuilder content: @escaping (Data.Element) -> Content) {
11+
self.items = data.map({ AnyView(content($0)) })
12+
self.spacing = spacing
13+
self.minimumItemWidth = minimumItemWidth
14+
}
15+
16+
public init<Data: RandomAccessCollection, ID, Item: View>(@ViewBuilder content: () -> Content) where Content == ForEach<Data, ID, Item> {
17+
let views = content()
18+
self.items = views.data.map { AnyView(views.content($0)) }
19+
self.minimumItemWidth = 300
20+
self.spacing = 8
21+
}
1022

1123
public var body: some View {
1224
GeometryReader { geometry in
1325
ScrollView {
1426
VStack(alignment: .leading, spacing: self.spacing) {
1527
ForEach(0..<self.rowCount(with: geometry), id: \.self) { rowIndex in
1628
HStack(spacing: self.spacing) {
17-
ForEach(0..<self.columnCount(with: geometry), id: \.self) { (columnIndex: Int) -> Content? in
29+
ForEach(0..<self.columnCount(with: geometry), id: \.self) { (columnIndex: Int) -> AnyView? in
1830
self.item(with: geometry, rowIndex: rowIndex, columnIndex: columnIndex)
1931
}
2032
.frame(maxWidth: .infinity)
21-
22-
ForEach(0..<self.emptyElementsCount(geometry: geometry, row: rowIndex), id: \.self) { _ in
23-
Spacer()
24-
}
2533
}
2634
}
2735
}
2836
.padding()
2937
}
30-
3138
}
3239
}
33-
34-
func emptyElementsCount(geometry:GeometryProxy, row: Int) -> Int {
35-
let isLastRow = rowCount(with: geometry) - 1 == row
36-
let possibleItemsCount = rowCount(with: geometry) * columnCount(with: geometry)
37-
return isLastRow ? possibleItemsCount % data.count : 0
38-
}
39-
40+
4041
func rowCount(with geometry: GeometryProxy) -> Int {
41-
Int((CGFloat(self.data.count) / max(1.0, geometry.size.width / self.minimumItemWidth).rounded(.down)).rounded(.up))
42+
Int((CGFloat(self.items.count) / max(1.0, geometry.size.width / self.minimumItemWidth).rounded(.down)).rounded(.up))
4243
}
4344

4445
func columnCount(with geometry: GeometryProxy) -> Int {
@@ -49,20 +50,14 @@ public struct Grid<Data, Content> : View where Data: RandomAccessCollection, Con
4950
rowIndex * max(1, Int(geometry.size.width / self.minimumItemWidth)) + columnIndex
5051
}
5152

52-
func item(with geometry: GeometryProxy, rowIndex: Int, columnIndex: Int) -> Content? {
53-
if self.itemIndex(with: geometry, rowIndex: rowIndex, columnIndex: columnIndex) <= self.data.count - 1 {
54-
return self.content(self.data[self.itemIndex(with: geometry, rowIndex: rowIndex, columnIndex: columnIndex)])
53+
func item(with geometry: GeometryProxy, rowIndex: Int, columnIndex: Int) -> AnyView {
54+
if self.itemIndex(with: geometry, rowIndex: rowIndex, columnIndex: columnIndex) <= self.items.count - 1 {
55+
return self.items[self.itemIndex(with: geometry, rowIndex: rowIndex, columnIndex: columnIndex)]
5556
} else {
56-
return nil
57+
return AnyView(Spacer())
5758
}
5859
}
59-
60-
public init(_ data: Data, minimumItemWidth: CGFloat = 300, spacing: CGFloat = 8, content: @escaping (Data.Element) -> Content) {
61-
self.data = data.map { $0 }
62-
self.content = content
63-
self.spacing = spacing
64-
self.minimumItemWidth = minimumItemWidth
65-
}
60+
6661
}
6762

6863
#if DEBUG

0 commit comments

Comments
 (0)