Skip to content

Instantly share code, notes, and snippets.

@satishVekariya
Last active November 13, 2023 14:39
Show Gist options
  • Save satishVekariya/eb72196e4289eb7b20a32d2b68afa7ee to your computer and use it in GitHub Desktop.
Save satishVekariya/eb72196e4289eb7b20a32d2b68afa7ee to your computer and use it in GitHub Desktop.
A SwiftUI wrapper of UICollectionView that manages an ordered collection of data items and presents them using customizable layouts.
//
// CollectionViewRepresentable.swift
//
//
// Created by Satish Vekariya on 12/11/2023.
//
import SwiftUI
import UIKit
/// A SwiftUI wrapper of UICollectionView that manages an ordered collection of data items and presents them using customizable layouts.
public struct CollectionViewRepresentable<Item, Content: View>: UIViewRepresentable {
@Binding var items: [Item]
let layout: LayoutKind
let alwaysBounceVertical: Bool
/// SwiftUI view provider
let contentProvider: (Item, IndexPath) -> Content
// Helper Callbacks
let shouldHighlightItemAt: ((UICollectionViewCell?, Item, IndexPath) -> Bool)?
let didHighlightItemAt: ((UICollectionViewCell?, Item, IndexPath) -> ())?
let didUnhighlightItemAt: ((UICollectionViewCell?, Item, IndexPath) -> ())?
let shouldSelectItemAt: ((UICollectionViewCell?, Item, IndexPath) -> Bool)?
let didSelectItemAt: ((UICollectionViewCell?, Item, IndexPath) -> ())?
public init(
_ items: Binding<[Item]>,
layout: LayoutKind,
alwaysBounceVertical: Bool = false,
@ViewBuilder contentProvider: @escaping (Item, IndexPath) -> Content,
shouldHighlightItemAt: ((UICollectionViewCell?, Item, IndexPath) -> Bool)? = nil,
didHighlightItemAt: ((UICollectionViewCell?, Item, IndexPath) -> ())? = nil,
didUnhighlightItemAt: ((UICollectionViewCell?, Item, IndexPath) -> ())? = nil,
shouldSelectItemAt: ((UICollectionViewCell?, Item, IndexPath) -> Bool)? = nil,
didSelectItemAt: ((UICollectionViewCell?, Item, IndexPath) -> ())? = nil
) {
self._items = items
self.layout = layout
self.alwaysBounceVertical = alwaysBounceVertical
self.contentProvider = contentProvider
self.shouldHighlightItemAt = shouldHighlightItemAt
self.didHighlightItemAt = didHighlightItemAt
self.didUnhighlightItemAt = didUnhighlightItemAt
self.shouldSelectItemAt = shouldSelectItemAt
self.didSelectItemAt = didSelectItemAt
}
public func makeUIView(context: Context) -> UICollectionView {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout.create())
collectionView.dataSource = context.coordinator
collectionView.delegate = context.coordinator
collectionView.alwaysBounceVertical = alwaysBounceVertical
collectionView.backgroundColor = nil
return collectionView
}
public func updateUIView(_ uiView: UICollectionView, context: Context) {
uiView.reloadData()
}
// Create a coordinator to handle the data source and delegate
public func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
public class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
var parent: CollectionViewRepresentable
private var userInteractionCellRegistration: UICollectionView.CellRegistration<UICollectionViewListCell, Item>!
init(_ parent: CollectionViewRepresentable) {
self.parent = parent
super.init()
userInteractionCellRegistration = .init { [unowned self] cell, indexPath, item in
cell.contentConfiguration = UIHostingConfiguration {
self.parent.contentProvider(item, indexPath)
}
.margins(.all, 0)
cell.backgroundConfiguration = .clear()
}
}
// UICollectionViewDataSource methods
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return parent.items.count
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item = parent.items[indexPath.row]
let cell = collectionView.dequeueConfiguredReusableCell(
using: userInteractionCellRegistration,
for: indexPath,
item: item
)
return cell
}
public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
let cell = collectionView.cellForItem(at: indexPath)
let item = parent.items[indexPath.row]
return parent.shouldHighlightItemAt?(cell, item, indexPath) ?? false
}
public func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
let item = parent.items[indexPath.row]
parent.didHighlightItemAt?(cell, item, indexPath)
}
public func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
let item = parent.items[indexPath.row]
parent.didUnhighlightItemAt?(cell, item, indexPath)
}
public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
let cell = collectionView.cellForItem(at: indexPath)
let item = parent.items[indexPath.row]
return parent.shouldHighlightItemAt?(cell, item, indexPath) ?? true
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
let item = parent.items[indexPath.row]
parent.didSelectItemAt?(cell, item, indexPath)
}
}
}
extension CollectionViewRepresentable {
public enum LayoutKind {
case list(appearance: UICollectionLayoutListConfiguration.Appearance)
case carousel
func create() -> UICollectionViewLayout {
switch self {
case .list(let appearance):
return UICollectionViewCompositionalLayout.list(using: .init(appearance: appearance))
case .carousel:
return CarouselLayoutGenerator.create()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment