Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Sources/CollectionVStack/CollectionVGrid+inits.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import SwiftUI

// MARK: Collection

public extension CollectionVGrid {
public extension CollectionVGrid where Header == EmptyView {

init(
uniqueElements: Data,
Expand Down Expand Up @@ -33,7 +33,7 @@ public extension CollectionVGrid {
}
}

public extension CollectionVGrid where Element: Identifiable, ID == Element.ID {
public extension CollectionVGrid where Element: Identifiable, ID == Element.ID, Header == EmptyView {

init(
uniqueElements: Data,
Expand Down Expand Up @@ -64,7 +64,7 @@ public extension CollectionVGrid where Element: Identifiable, ID == Element.ID {

// MARK: Count

public extension CollectionVGrid where Data == [Element], Element == Int, ID == Int {
public extension CollectionVGrid where Data == [Element], Element == Int, ID == Int, Header == EmptyView {

init(
count: Int,
Expand Down
18 changes: 18 additions & 0 deletions Sources/CollectionVStack/CollectionVGrid+modifiers.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import SwiftUI

public extension CollectionVGrid {

func onReachedBottomEdge(
Expand All @@ -24,6 +26,22 @@ public extension CollectionVGrid {
copy(modifying: \.onCancelPrefetchingElements, to: action)
}

func header<NewHeader: View>(@ViewBuilder _ headerProvider: @escaping () -> NewHeader) -> CollectionVGrid<Element, Data, ID, Content, NewHeader> {
CollectionVGrid<Element, Data, ID, Content, NewHeader>(
id: _id,
data: data,
layout: layout,
onReachedBottomEdge: onReachedBottomEdge,
onReachedBottomEdgeOffset: onReachedBottomEdgeOffset,
onReachedTopEdge: onReachedTopEdge,
onReachedTopEdgeOffset: onReachedTopEdgeOffset,
onPrefetchingElements: onPrefetchingElements,
onCancelPrefetchingElements: onCancelPrefetchingElements,
headerProvider: headerProvider,
viewProvider: viewProvider
)
}

func proxy(_ proxy: CollectionVGridProxy) -> Self {
copy(modifying: \.proxy, to: proxy)
}
Expand Down
11 changes: 8 additions & 3 deletions Sources/CollectionVStack/CollectionVGrid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ public struct CollectionVGrid<
Element,
Data: Collection,
ID: Hashable,
Content: View
Content: View,
Header: View
>: UIViewRepresentable where Data.Element == Element,
Data.Index == Int {

public typealias UIViewType = UICollectionVGrid<Element, Data, ID, Content>
public typealias UIViewType = UICollectionVGrid<Element, Data, ID, Content, Header>

let _id: KeyPath<Element, ID>
let data: Data
Expand All @@ -21,9 +22,10 @@ Data.Index == Int {
var onPrefetchingElements: ([Element]) -> Void
var onCancelPrefetchingElements: ([Element]) -> Void
var proxy: CollectionVGridProxy?
var headerProvider: (() -> Header)?
let viewProvider: (Element, CollectionVGridLocation) -> Content

init(
public init(
id: KeyPath<Element, ID>,
data: Data,
layout: CollectionVGridLayout,
Expand All @@ -33,6 +35,7 @@ Data.Index == Int {
onReachedTopEdgeOffset: CollectionVGridEdgeOffset = .offset(0),
onPrefetchingElements: @escaping ([Element]) -> Void = { _ in },
onCancelPrefetchingElements: @escaping ([Element]) -> Void = { _ in },
headerProvider: (() -> Header)? = nil,
@ViewBuilder viewProvider: @escaping (Element, CollectionVGridLocation) -> Content
) {
self._id = id
Expand All @@ -44,6 +47,7 @@ Data.Index == Int {
self.onReachedTopEdgeOffset = onReachedTopEdgeOffset
self.onPrefetchingElements = onPrefetchingElements
self.onCancelPrefetchingElements = onCancelPrefetchingElements
self.headerProvider = headerProvider
self.viewProvider = viewProvider
}

Expand All @@ -59,6 +63,7 @@ Data.Index == Int {
onPrefetchingElements: onPrefetchingElements,
onCancelPrefetchingElements: onCancelPrefetchingElements,
proxy: proxy,
headerProvider: headerProvider,
viewProvider: viewProvider
)
}
Expand Down
67 changes: 66 additions & 1 deletion Sources/CollectionVStack/UICollectionVGrid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class UICollectionVGrid<
Element,
Data: Collection,
ID: Hashable,
Content: View
Content: View,
Header: View
>:
UIView,
_UICollectionVGrid,
Expand All @@ -50,6 +51,11 @@ public class UICollectionVGrid<
private let onPrefetchingElements: ([Element]) -> Void
private let onCancelPrefetchingElements: ([Element]) -> Void
private let viewProvider: (Element, CollectionVGridLocation) -> Content
private let headerProvider: (() -> Header)?
private var headerSize: CGSize?

private let headerReuseIdentifier = "HostingCollectionViewHeader"


// MARK: init

Expand All @@ -64,6 +70,7 @@ public class UICollectionVGrid<
onPrefetchingElements: @escaping ([Element]) -> Void,
onCancelPrefetchingElements: @escaping ([Element]) -> Void,
proxy: CollectionVGridProxy?,
headerProvider: (() -> Header)?,
viewProvider: @escaping (Element, CollectionVGridLocation) -> Content
) {
self._id = id
Expand All @@ -78,6 +85,7 @@ public class UICollectionVGrid<
self.onPrefetchingElements = onPrefetchingElements
self.onCancelPrefetchingElements = onCancelPrefetchingElements
self.onReachedEdgeStore = []
self.headerProvider = headerProvider
self.viewProvider = viewProvider

super.init(frame: .zero)
Expand Down Expand Up @@ -105,6 +113,11 @@ public class UICollectionVGrid<
HostingCollectionViewCell<Content>.self,
forCellWithReuseIdentifier: cellReuseIdentifier
)
collectionView.register(
HostingCollectionViewCell<Header>.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: headerReuseIdentifier
)
collectionView.dataSource = self
collectionView.prefetchDataSource = self
collectionView.delegate = self
Expand All @@ -129,6 +142,8 @@ public class UICollectionVGrid<
super.layoutSubviews()

itemSize = nil
headerSize = nil

collectionView.performBatchUpdates {
collectionView.flowLayout.invalidateLayout()
}
Expand Down Expand Up @@ -172,6 +187,7 @@ public class UICollectionVGrid<
layout = newLayout

itemSize = nil
headerSize = nil

collectionView.flowLayout.sectionInset = newLayout.insets.asUIEdgeInsets
collectionView.flowLayout.minimumLineSpacing = newLayout.lineSpacing
Expand Down Expand Up @@ -205,6 +221,7 @@ public class UICollectionVGrid<

collectionView.alpha = 0
itemSize = nil
headerSize = nil
collectionView.reloadData()

UIView.animate(withDuration: 0.1) {
Expand Down Expand Up @@ -240,6 +257,27 @@ public class UICollectionVGrid<
return cell
}

public func collectionView(
_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath
) -> UICollectionReusableView {
guard kind == UICollectionView.elementKindSectionHeader,
let headerProvider = headerProvider else {
return UICollectionReusableView()
}

let header = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: headerReuseIdentifier,
for: indexPath
) as! HostingCollectionViewCell<Header>

header.setup(view: headerProvider())

return header
}

// MARK: UICollectionViewDelegate

// required for tvOS
Expand Down Expand Up @@ -283,6 +321,33 @@ public class UICollectionVGrid<
}
}

public func collectionView(
_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
referenceSizeForHeaderInSection section: Int
) -> CGSize {
guard headerProvider != nil else {
return .zero
}

if let headerSize {
return headerSize
} else {
let width = collectionView.bounds.width
let view = headerProvider!()
let hostingController = UIHostingController(rootView: view.frame(width: width))
hostingController.view.sizeToFit()

let size = CGSize(
width: width,
height: hostingController.view.bounds.height
)

headerSize = size
return size
}
}

// MARK: UIScrollViewDelegate

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
Expand Down