Skip to content

Commit 99bf7dd

Browse files
authored
SelectableForEach (#31)
1 parent 1361282 commit 99bf7dd

File tree

4 files changed

+186
-157
lines changed

4 files changed

+186
-157
lines changed
Lines changed: 77 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import CollectionView
21
import SwiftUI
32

3+
@testable import CollectionView
4+
45
struct BookCollectionViewSingleSection: View, PreviewProvider {
56

67
var body: some View {
@@ -18,17 +19,19 @@ struct BookCollectionViewSingleSection: View, PreviewProvider {
1819

1920
var body: some View {
2021
CollectionView(
21-
dataSource: .collection(
22-
data: Item.mock(),
23-
selection: .single(
24-
selected: selected?.id,
25-
onChange: { e in
26-
selected = e
27-
}),
28-
cell: { index, item in
29-
Cell(index: index, item: item)
30-
}
31-
),
22+
content: {
23+
SelectableForEach(
24+
data: Item.mock(),
25+
selection: .single(
26+
selected: selected?.id,
27+
onChange: { e in
28+
selected = e
29+
}),
30+
cell: { index, item in
31+
Cell(index: index, item: item)
32+
}
33+
)
34+
},
3235
layout: .list {
3336
RoundedRectangle(cornerRadius: 8)
3437
.fill(.secondary)
@@ -59,7 +62,7 @@ struct BookCollectionViewCombined: View, PreviewProvider {
5962

6063
var body: some View {
6164
CollectionView(
62-
dataSource: CollectionViewDataSources.Unified {
65+
content: {
6366

6467
Text("Static content")
6568
.overlay(content: {
@@ -68,7 +71,7 @@ struct BookCollectionViewCombined: View, PreviewProvider {
6871

6972
Text("📱❄️")
7073

71-
CollectionViewDataSources.UsingCollection(
74+
SelectableForEach(
7275
data: Item.mock(10),
7376
selection: .single(
7477
selected: selected?.id,
@@ -82,7 +85,7 @@ struct BookCollectionViewCombined: View, PreviewProvider {
8285

8386
Text("📱❄️")
8487

85-
CollectionViewDataSources.UsingCollection(
88+
SelectableForEach(
8689
data: Item.mock(10),
8790
selection: .single(
8891
selected: selected2?.id,
@@ -110,19 +113,6 @@ struct BookCollectionViewCombined: View, PreviewProvider {
110113

111114
}
112115

113-
#Preview {
114-
CollectionViewDataSources.UsingCollection(
115-
data: Item.mock(10),
116-
selection: .disabled(),
117-
cell: { index, item in
118-
Cell(index: index, item: item)
119-
}
120-
)
121-
}
122-
//#Preview {
123-
// BookPreview()
124-
//}
125-
126116
#Preview("Custom List / Single selection") {
127117

128118
struct Book: View {
@@ -131,17 +121,19 @@ struct BookCollectionViewCombined: View, PreviewProvider {
131121

132122
var body: some View {
133123
CollectionView(
134-
dataSource: CollectionViewDataSources.UsingCollection(
135-
data: Item.mock(),
136-
selection: .single(
137-
selected: selected?.id,
138-
onChange: { e in
139-
selected = e
140-
}),
141-
cell: { index, item in
142-
Cell(index: index, item: item)
143-
}
144-
),
124+
content: {
125+
SelectableForEach(
126+
data: Item.mock(),
127+
selection: .single(
128+
selected: selected?.id,
129+
onChange: { e in
130+
selected = e
131+
}),
132+
cell: { index, item in
133+
Cell(index: index, item: item)
134+
}
135+
)
136+
},
145137
layout: .list {
146138
RoundedRectangle(cornerRadius: 8)
147139
.fill(.secondary)
@@ -164,23 +156,25 @@ struct BookCollectionViewCombined: View, PreviewProvider {
164156
var body: some View {
165157

166158
CollectionView(
167-
dataSource: CollectionViewDataSources.UsingCollection(
168-
data: Item.mock(),
169-
selection: .multiple(
170-
selected: selected,
171-
canMoreSelect: selected.count < 3,
172-
onChange: { e, action in
173-
switch action {
174-
case .selected:
175-
selected.insert(e.id)
176-
case .deselected:
177-
selected.remove(e.id)
178-
}
179-
}),
180-
cell: { index, item in
181-
Cell(index: index, item: item)
182-
}
183-
),
159+
content: {
160+
SelectableForEach(
161+
data: Item.mock(),
162+
selection: .multiple(
163+
selected: selected,
164+
canMoreSelect: selected.count < 3,
165+
onChange: { e, action in
166+
switch action {
167+
case .selected:
168+
selected.insert(e.id)
169+
case .deselected:
170+
selected.remove(e.id)
171+
}
172+
}),
173+
cell: { index, item in
174+
Cell(index: index, item: item)
175+
}
176+
)
177+
},
184178
layout: .list {
185179
RoundedRectangle(cornerRadius: 8)
186180
.fill(.secondary)
@@ -197,16 +191,18 @@ struct BookCollectionViewCombined: View, PreviewProvider {
197191
#Preview("SwiftUI List") {
198192

199193
CollectionView(
200-
dataSource: CollectionViewDataSources.UsingCollection(
201-
data: Item.mock(),
202-
selection: .disabled(),
203-
cell: { index, item in
204-
HStack {
205-
Text(index.description)
206-
Text(item.title)
194+
content: {
195+
SelectableForEach(
196+
data: Item.mock(),
197+
selection: .disabled(),
198+
cell: { index, item in
199+
HStack {
200+
Text(index.description)
201+
Text(item.title)
202+
}
207203
}
208-
}
209-
),
204+
)
205+
},
210206
layout: CollectionViewLayouts.PlatformList()
211207
)
212208
}
@@ -246,84 +242,27 @@ struct BookCollectionViewCombined: View, PreviewProvider {
246242
return BookList()
247243
}
248244

249-
struct Item: Identifiable {
250-
var id: Int
251-
var title: String
252-
253-
static func mock(_ count: Int = 1000) -> [Item] {
254-
return (0..<count).map { index in
255-
Item(id: index, title: "Item \(index)")
256-
}
257-
}
258-
}
259-
260-
struct Cell: View {
261-
262-
@Environment(\.isEnabled) var isEnabled
263-
@Environment(\.collectionView_isSelected) var isSelected
264-
@Environment(\.collectionView_updateSelection) var updateSelection
265-
266-
let index: Int
267-
let item: Item
268-
269-
init(index: Int, item: Item) {
245+
#Preview("SelectableForEach") {
270246

271-
print("Cell init \(index), \(item.title)")
247+
struct Book: View {
272248

273-
self.index = index
274-
self.item = item
275-
}
249+
@State var selected: Item?
276250

277-
var body: some View {
278-
HStack {
279-
Circle()
280-
.fill(.red)
281-
.frame(width: 20, height: 20)
282-
.opacity(isSelected ? 1 : 0.2)
283-
Text(index.description)
284-
Text(item.title)
285-
Text("isEnabled: \(isEnabled)")
251+
var body: some View {
252+
SelectableForEach(
253+
data: Item.mock(10),
254+
selection: .single(
255+
selected: selected?.id,
256+
onChange: { e in
257+
selected = e
258+
}),
259+
cell: { index, item in
260+
Cell(index: index, item: item)
261+
}
262+
)
286263
}
287-
._onButtonGesture(
288-
pressing: { _ in },
289-
perform: {
290-
updateSelection(!isSelected)
291-
})
292-
}
293-
}
294-
295-
private struct ConfirmingSingle<Item: Identifiable>: CollectionViewSelection {
296-
297-
private let selected: Item.ID?
298-
private let onChange: (_ selected: Item?) -> Void
299-
private let canSelect: (_ item: Item) -> Bool
300-
301-
public init(
302-
selected: Item.ID?,
303-
canSelect: @escaping (_ item: Item) -> Bool,
304-
onChange: @escaping (_ selected: Item?) -> Void
305-
) {
306-
self.selected = selected
307-
self.onChange = onChange
308-
self.canSelect = canSelect
309264
}
310265

311-
public func isSelected(for id: Item.ID) -> Bool {
312-
self.selected == id
313-
}
314-
315-
public func isEnabled(for id: Item.ID) -> Bool {
316-
return true
317-
}
318-
319-
public func update(isSelected: Bool, for item: Item) {
320-
if isSelected {
321-
if canSelect(item) {
322-
onChange(item)
323-
}
324-
} else {
325-
onChange(nil)
326-
}
327-
}
266+
return Book()
328267

329268
}
Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,28 @@
11
import IndexedCollection
22
import SwiftUI
33

4-
5-
/// Still searching better name
6-
/// - built on top of SwiftUI only
7-
@available(iOS 16, *)
84
public struct CollectionView<
9-
DataSource: CollectionViewDataSource,
5+
Content: View,
106
Layout: CollectionViewLayoutType
117
>: View {
128

13-
private let dataSource: DataSource
9+
private let content: Content
10+
1411
private let layout: Layout
1512

1613
public init(
17-
dataSource: DataSource,
14+
@ViewBuilder content: () -> Content,
1815
layout: Layout
1916
) {
20-
self.dataSource = dataSource
17+
self.content = content()
2118
self.layout = layout
19+
self.items = items
20+
self.selection = .init()
2221
}
2322

2423
public var body: some View {
25-
26-
self.dataSource
24+
content
2725
.modifier(layout)
28-
2926
}
3027

3128
}
32-
33-
extension EnvironmentValues {
34-
@Entry public var collectionView_isSelected: Bool = false
35-
}
36-
37-
extension EnvironmentValues {
38-
@Entry public var collectionView_updateSelection: (Bool) -> Void = { _ in }
39-
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
#if DEBUG
3+
4+
import SwiftUI
5+
6+
struct Item: Identifiable {
7+
var id: Int
8+
var title: String
9+
10+
static func mock(_ count: Int = 1000) -> [Item] {
11+
return (0..<count).map { index in
12+
Item(id: index, title: "Item \(index)")
13+
}
14+
}
15+
}
16+
17+
struct Cell: View {
18+
19+
@Environment(\.isEnabled) var isEnabled
20+
@Environment(\.collectionView_isSelected) var isSelected
21+
@Environment(\.collectionView_updateSelection) var updateSelection
22+
23+
let index: Int
24+
let item: Item
25+
26+
init(index: Int, item: Item) {
27+
28+
print("Cell init \(index), \(item.title)")
29+
30+
self.index = index
31+
self.item = item
32+
}
33+
34+
var body: some View {
35+
HStack {
36+
Circle()
37+
.fill(.red)
38+
.frame(width: 20, height: 20)
39+
.opacity(isSelected ? 1 : 0.2)
40+
Text(index.description)
41+
Text(item.title)
42+
Text("isEnabled: \(isEnabled)")
43+
}
44+
._onButtonGesture(
45+
pressing: { _ in },
46+
perform: {
47+
updateSelection(!isSelected)
48+
})
49+
}
50+
}
51+
#endif

0 commit comments

Comments
 (0)