Skip to content

Commit e20b194

Browse files
authored
Refactor/rewrite using variadic view (GeorgeElsham#10)
* Rewrite entire package using _VariadicView children * Update README for new package API * Add images for examples in README
1 parent 09cc1e7 commit e20b194

File tree

5 files changed

+166
-204
lines changed

5 files changed

+166
-204
lines changed

README.md

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,26 @@ Follow the instructions at [Adding Package Dependencies to Your App][1] to find
1010

1111
## Example #1
1212

13-
Here is an example which creates a `VStack` with `Divider()`s in between.
13+
Create a vertical stack of views, separated by dividers.
1414

1515
### View code
1616

1717
```swift
18-
struct DividedVStack: View {
19-
private let views: [AnyView]
20-
21-
// For 2 or more views
22-
init<Views>(@ViewBuilder content: TupleContent<Views>) {
23-
views = ViewExtractor.getViews(from: content)
24-
}
25-
26-
// For 0 or 1 view
27-
init<Content: View>(@ViewBuilder content: NormalContent<Content>) {
28-
views = ViewExtractor.getViews(from: content)
29-
}
30-
18+
struct DividedVStack<Content: View>: View {
19+
@ViewBuilder let content: Content
20+
3121
var body: some View {
32-
VStack(spacing: 0) {
33-
ForEach(views.indices) { index in
34-
if index != 0 {
35-
Divider()
22+
Extract(content) { views in
23+
VStack {
24+
let first = views.first?.id
25+
26+
ForEach(views) { view in
27+
if view.id != first {
28+
Divider()
29+
}
30+
31+
view
3632
}
37-
38-
views[index]
3933
}
4034
}
4135
}
@@ -49,34 +43,31 @@ DividedVStack {
4943
Text("View 1")
5044
Text("View 2")
5145
Text("View 3")
52-
Image(systemName: "circle")
5346
}
5447
```
5548

5649
### Result
5750

58-
<img src="https://user-images.githubusercontent.com/40073010/115965850-f43c5d80-a522-11eb-8113-1f73d07fade0.png" width="25%">
51+
<img width="400" alt="Example #1 result" src="https://user-images.githubusercontent.com/40073010/202593448-02ad4069-0c9a-4bff-85cd-ddd9313b3c51.png">
5952

6053

6154
## Example #2
6255

63-
Here is an example which creates a `VStack` of views, for every multiple of 2. The views are loaded lazily, so they aren't all computed at once which would waste resources if half of them aren't used.
56+
Create a vertical stack of views, only keeping views at indexes with a multiple of 2.
6457

6558
### View code
6659

6760
```swift
68-
struct IntervalVStack: View {
69-
private let extractor: ViewExtractor
70-
71-
init<Content: View & DynamicViewContentProvider>(content: ForEachContent<Content>) {
72-
extractor = ViewExtractor(content: content)
73-
}
61+
struct IntervalVStack<Content: View>: View {
62+
@ViewBuilder let content: Content
7463

7564
var body: some View {
76-
VStack(spacing: 0) {
77-
ForEach(extractor.forEachRange!) { index in
78-
if index.isMultiple(of: 2) {
79-
extractor.uncheckedView(at: index)
65+
Extract(content) { views in
66+
VStack {
67+
ForEach(Array(zip(views.indices, views)), id: \.1.id) { index, view in
68+
if index.isMultiple(of: 2) {
69+
view
70+
}
8071
}
8172
}
8273
}
@@ -96,7 +87,51 @@ IntervalVStack {
9687

9788
### Result
9889

99-
<img src="https://user-images.githubusercontent.com/40073010/127054749-e5d27295-f179-41bd-9517-3b2d0eb4b970.png" width="25%">
90+
<img width="400" alt="Example #2 result" src="https://user-images.githubusercontent.com/40073010/202593730-594d45f9-6abf-46a8-8dba-1f93ec89e55d.png">
91+
92+
93+
## Example #3
94+
95+
Create a stack of views, returning multiple views as a result, allowing it to be wrapped by another view. When modifiers are applied, they apply to each view returned. Note the use of `ExtractMulti` rather than just `Extract`.
96+
97+
### View code
98+
99+
```swift
100+
struct Divided<Content: View>: View {
101+
@ViewBuilder let content: Content
102+
103+
var body: some View {
104+
ExtractMulti(content) { views in
105+
let first = views.first?.id
106+
107+
ForEach(views) { view in
108+
if view.id != first {
109+
Divider()
110+
}
111+
112+
view
113+
}
114+
}
115+
}
116+
}
117+
```
118+
119+
### Usage
120+
121+
```swift
122+
HStack {
123+
Divided {
124+
Text("View 1")
125+
Text("View 2")
126+
Text("View 3")
127+
}
128+
.border(Color.red)
129+
}
130+
```
131+
132+
### Result
133+
134+
<img width="400" alt="Example #3 result" src="https://user-images.githubusercontent.com/40073010/202593772-bf61b3bb-3d64-4d5f-8a57-1132ed2ba2d2.png">
100135

101136

102137
[1]: https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app#3234996
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Extract.swift
3+
//
4+
//
5+
// Created by George Elsham on 17/11/2022.
6+
//
7+
8+
import SwiftUI
9+
10+
public struct Extract<Content: View, ViewsContent: View>: Extractable {
11+
let content: () -> Content
12+
let views: (Views) -> ViewsContent
13+
14+
public init(_ content: Content, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
15+
self.content = { content }
16+
self.views = views
17+
}
18+
19+
public init(@ViewBuilder _ content: @escaping () -> Content, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
20+
self.content = content
21+
self.views = views
22+
}
23+
24+
public var body: some View {
25+
_VariadicView.Tree(
26+
UnaryViewRoot(views: views),
27+
content: content
28+
)
29+
}
30+
}
31+
32+
fileprivate struct UnaryViewRoot<Content: View>: _VariadicView_UnaryViewRoot {
33+
let views: (Views) -> Content
34+
35+
func body(children: Views) -> some View {
36+
views(children)
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// ExtractMulti.swift
3+
//
4+
//
5+
// Created by George Elsham on 17/11/2022.
6+
//
7+
8+
import SwiftUI
9+
10+
public struct ExtractMulti<Content: View, ViewsContent: View>: Extractable {
11+
let content: () -> Content
12+
let views: (Views) -> ViewsContent
13+
14+
public init(_ content: Content, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
15+
self.content = { content }
16+
self.views = views
17+
}
18+
19+
public init(@ViewBuilder _ content: @escaping () -> Content, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
20+
self.content = content
21+
self.views = views
22+
}
23+
24+
public var body: some View {
25+
_VariadicView.Tree(
26+
MultiViewRoot(views: views),
27+
content: content
28+
)
29+
}
30+
}
31+
32+
fileprivate struct MultiViewRoot<Content: View>: _VariadicView_MultiViewRoot {
33+
let views: (Views) -> Content
34+
35+
func body(children: Views) -> some View {
36+
views(children)
37+
}
38+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// Extractable.swift
3+
//
4+
//
5+
// Created by George Elsham on 17/11/2022.
6+
//
7+
8+
import SwiftUI
9+
10+
protocol Extractable: View {
11+
associatedtype Content: View
12+
associatedtype ViewsContent: View
13+
14+
var content: () -> Content { get }
15+
var views: (Views) -> ViewsContent { get }
16+
17+
init(_ content: Content, @ViewBuilder views: @escaping (Views) -> ViewsContent)
18+
init(@ViewBuilder _ content: @escaping () -> Content, @ViewBuilder views: @escaping (Views) -> ViewsContent)
19+
}
20+
21+
public typealias Views = _VariadicView.Children

0 commit comments

Comments
 (0)