Skip to content

Instantly share code, notes, and snippets.

@algal
Created March 8, 2017 18:34
Show Gist options
  • Save algal/bc22e837c83d0bb299a344c89b8be32e to your computer and use it in GitHub Desktop.
Save algal/bc22e837c83d0bb299a344c89b8be32e to your computer and use it in GitHub Desktop.
// known-good: Swift 3
import UIKit
/// for a UIView that can be populated via `configure`
protocol ConfigurableView : class {
associatedtype Value
func configure(_ value:Value)
}
/// for a UICollectionViewCell wrapping a UIView
protocol ConfigurableCell : class {
associatedtype View : ConfigurableView
var wrappedView:View { get }
static var classReuseIdentifier:String { get }
}
/// Generic UICollectionViewCell, for UIViews that conform to ConfigurableView
class WrappedCollectionViewCell<T:UIView> : UICollectionViewCell, ConfigurableCell
where T:ConfigurableView
{
func setup()
{
let wrappedView = T.init(frame: self.contentView.bounds)
self.contentView.addSubview(wrappedView)
wrappedView.translatesAutoresizingMaskIntoConstraints = false
for vfl in ["V:|[v]|","H:|[v]|"]
{
let cs = NSLayoutConstraint.constraints(withVisualFormat: vfl, options: [], metrics: nil, views: ["v":wrappedView])
self.contentView.addConstraints(cs)
}
}
var wrappedView:T {
return self.contentView.subviews.first! as! T
}
class var classReuseIdentifier:String {
return "\(T.self)" + "CellIdentifier"
}
override class var requiresConstraintBasedLayout:Bool { return true }
override init(frame:CGRect)
{
super.init(frame:frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
setup()
}
}
/**
Generic single-row, horizontal scrolling collection view.
This is generic over the data type and the view that displays a data item.
This works, but maybe is too generic/clever for real use.
How to use:
1. Choose a value type V, like Int
2. Define a UIView subtype, like MyView, adopting to ConfigurableView
3. Instantiate the VC via HorizontalGalleryVC<Int,MyView>(items: hundred)
*/
class HorizontalGalleryVC<V,View:UIView> : UICollectionViewController
where View.Value==V, View:ConfigurableView
{
var items:[V] = []
typealias Cell = WrappedCollectionViewCell<View>
let interItemHorizontalSpace:CGFloat = 20
let topPadding:CGFloat = 10
let bottomPadding:CGFloat = 10
init(items:[V]) {
self.items = items
let lay = UICollectionViewFlowLayout()
lay.scrollDirection = .horizontal
lay.minimumLineSpacing = self.interItemHorizontalSpace
lay.minimumInteritemSpacing = CGFloat.greatestFiniteMagnitude // force one row
lay.sectionInset = UIEdgeInsets(top: self.topPadding,
left: 0,
bottom: self.bottomPadding,
right: 0)
super.init(collectionViewLayout: lay)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad()
{
super.viewDidLoad()
self.collectionView!.isOpaque = true
self.collectionView!.backgroundColor = .white
self.collectionView!.register(Cell.self, forCellWithReuseIdentifier: Cell.classReuseIdentifier)
let cv = self.collectionView!
cv.allowsSelection = true
cv.allowsMultipleSelection = false
cv.scrollsToTop = false
cv.isPagingEnabled = false
let flowLayout = self.collectionView!.collectionViewLayout as! UICollectionViewFlowLayout
flowLayout.itemSize = CGSize(width: 59, height: 50)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = self.collectionView!.dequeueReusableCell(withReuseIdentifier: Cell.classReuseIdentifier,
for: indexPath)
let configurableCell = cell as! Cell
configurableCell.wrappedView.configure(self.items[indexPath.row])
return cell
}
override func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
NSLog("tapped")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment