Skip to content

Instantly share code, notes, and snippets.

@westerlund
Created March 21, 2020 21:44
Show Gist options
  • Save westerlund/6008beccc4ae7ce3b923e35a4d6585ab to your computer and use it in GitHub Desktop.
Save westerlund/6008beccc4ae7ce3b923e35a4d6585ab to your computer and use it in GitHub Desktop.
//
// SwiftUICollectionView.swift
// Sentry
//
// Created by Simon Westerlund on 2019-12-29.
// Copyright © 2019 Simon Westerlund. All rights reserved.
//
import SwiftUI
final class UIHostingControllerCollectionViewCell<Content: View>: UICollectionViewCell {
private var view: UIView? {
willSet {
view?.removeFromSuperview()
}
}
override func prepareForReuse() {
super.prepareForReuse()
view?.removeFromSuperview()
}
func configureForDisplay(with item: Content) {
view = UIHostingController(rootView: item).view
embedView(view)
}
func select(with item: Content) {
let selectedItem = item.modifier(SelectedViewModifier(selected: true))
view = UIHostingController(rootView: selectedItem).view
embedView(view)
}
private func embedView(_ view: UIView?) {
guard let view = view else {
return
}
contentView.addSubview(view)
contentView.backgroundColor = .clear
backgroundColor = .clear
view.frame = contentView.bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
}
final class DataSource<Content: View>: NSObject, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@Binding var selectedIndexPath: IndexPath
let items: [Content]
init(items: [Content], selectedIndexPath: Binding<IndexPath>) {
self.items = items
self._selectedIndexPath = selectedIndexPath
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UIHostingControllerCollectionViewCell<Content>
let item = items[indexPath.row]
if selectedIndexPath == indexPath {
cell.select(with: item)
} else {
cell.configureForDisplay(with: item)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = collectionView.frame.height
return CGSize(width: height * 4 / 3, height: height)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: selectedIndexPath) as? UIHostingControllerCollectionViewCell<Content> {
let item = items[selectedIndexPath.row]
cell.configureForDisplay(with: item)
}
selectedIndexPath = indexPath
if let cell = collectionView.cellForItem(at: selectedIndexPath) as? UIHostingControllerCollectionViewCell<Content> {
let item = items[selectedIndexPath.row]
cell.select(with: item)
}
}
}
struct SwiftUICollectionView<Content: View>: UIViewRepresentable {
typealias UIViewType = UICollectionView
let dataSource: DataSource<Content>
let collectionView: UIViewType
init(cells: [Content], selectedIndexPath: Binding<IndexPath>) {
dataSource = DataSource(items: cells, selectedIndexPath: selectedIndexPath)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
}
func makeUIView(context: UIViewRepresentableContext<SwiftUICollectionView>) -> UICollectionView {
collectionView.register(UIHostingControllerCollectionViewCell<Content>.self, forCellWithReuseIdentifier: "cell")
collectionView.dataSource = dataSource
collectionView.delegate = dataSource
collectionView.backgroundColor = .clear
return collectionView
}
func updateUIView(_ uiView: UICollectionView, context: UIViewRepresentableContext<SwiftUICollectionView>) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject {
var control: SwiftUICollectionView
init(_ control: SwiftUICollectionView) {
self.control = control
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment