Last active
February 11, 2020 03:08
-
-
Save kazuhiro4949/230268f146e891540d4849b963dbe6cd to your computer and use it in GitHub Desktop.
UICollectionViewController including type erased delegate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import UIKit | |
import XCPlayground | |
class MyViewController<Delegate: MyViewControllerDelegate>: UICollectionViewController, UICollectionViewDelegateFlowLayout { | |
var delegate: AnyMyViewControllerDelegate<Delegate>? | |
var items = [Array<Delegate.Item>]() { | |
didSet { | |
collectionView?.reloadData() | |
} | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "identifier") | |
} | |
init(_ delegate: AnyMyViewControllerDelegate<Delegate>, configure: (register: (cellClass: AnyClass, identifier: String) -> Void) -> Void = { _ in }) { | |
super.init(collectionViewLayout: UICollectionViewFlowLayout()) | |
let register = { [weak self] (cellClass: AnyClass, identifier: String) -> Void in | |
self?.collectionView?.registerClass(cellClass, forCellWithReuseIdentifier: identifier) | |
} | |
self.delegate = delegate | |
configure(register: register) | |
} | |
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { | |
func dequeue(from indexPath: NSIndexPath) -> ((identifier: String) -> UICollectionViewCell?) { | |
return { (identifier: String) -> UICollectionViewCell? in | |
return collectionView.dequeueReusableCellWithReuseIdentifier(identifier, forIndexPath: indexPath) | |
} | |
} | |
collectionView.dequeueReusableCellWithReuseIdentifier("identifier", forIndexPath: indexPath) | |
let dequeueHandler = dequeue(from: indexPath) | |
let item = items[indexPath.section][indexPath.row] | |
guard let cell = delegate?.myViewController(self, dequeue: dequeueHandler, cellFor: item) else { | |
fatalError("cannot get cell") | |
} | |
return cell | |
} | |
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | |
return items[section].count | |
} | |
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { | |
delegate?.myViewController(self, didSelect: items[indexPath.section][indexPath.row]) | |
} | |
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { | |
return items.count | |
} | |
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { | |
let contentSize = delegate?.myViewController(self, layout: collectionViewLayout, sizeFor: items[indexPath.section][indexPath.row]) | |
return contentSize ?? collectionViewLayout.collectionViewContentSize() | |
} | |
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat { | |
return 1 | |
} | |
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat { | |
return 0 | |
} | |
} | |
class AnyMyViewControllerDelegate<Delegate: MyViewControllerDelegate> { | |
private weak var delegate: Delegate? | |
init(delegate: Delegate) { | |
self.delegate = delegate | |
} | |
func myViewController(vc: MyViewController<Delegate>, dequeue: (identifier: String) -> UICollectionViewCell?, cellFor item: Delegate.Item) -> UICollectionViewCell? { | |
return delegate?.myViewController(vc, dequeue: dequeue, cellFor: item) | |
} | |
func myViewController(vc: MyViewController<Delegate>, layout collectionViewLayout: UICollectionViewLayout, sizeFor item: Delegate.Item) -> CGSize? { | |
return delegate?.myViewController(vc, layout: collectionViewLayout, sizeFor: item) | |
} | |
func myViewController(vc: MyViewController<Delegate>, didSelect item: Delegate.Item) { | |
delegate?.myViewController(vc, didSelect: item) | |
} | |
} | |
protocol MyViewControllerDelegate: class { | |
associatedtype Item | |
func myViewController(vc: MyViewController<Self>, dequeue: ((identifier: String) -> UICollectionViewCell?), cellFor item: Item) -> UICollectionViewCell? | |
func myViewController(vc: MyViewController<Self>, layout collectionViewLayout: UICollectionViewLayout, sizeFor item: Item) -> CGSize? | |
func myViewController(vc: MyViewController<Self>, didSelect item: Item) | |
} | |
extension MyViewControllerDelegate { | |
func myViewController(vc: MyViewController<Self>, layout collectionViewLayout: UICollectionViewLayout, sizeFor item: Item) -> CGSize? { return nil } | |
func myViewController(vc: MyViewController<Self>, didSelect item: Item) {} | |
} | |
// ################################################# | |
// Create sample ViewController the above Framework | |
// It has two types of cell in CollectionView. | |
// ################################################# | |
enum Element { | |
case Banner(name: String) | |
case Content(name: String) | |
} | |
final class ViewController: UIViewController, MyViewControllerDelegate { | |
typealias Item = Element | |
lazy var childVc: MyViewController<ViewController> = { | |
return MyViewController(AnyMyViewControllerDelegate(delegate: self)) { (register) in | |
register(cellClass: UICollectionViewCell.self, identifier: "cell") | |
} | |
}() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
title = "Title" | |
addChildViewController(childVc) | |
childVc.view.frame = view.bounds | |
childVc.view.autoresizingMask = [.FlexibleHeight, .FlexibleWidth] | |
view.addSubview(childVc.view) | |
childVc.didMoveToParentViewController(self) | |
childVc.items = [[ | |
.Banner(name: "バナー"), | |
.Content(name: "コンテンツ"), .Content(name: "コンテンツ"), .Content(name: "コンテンツ"), .Content(name: "コンテンツ"), | |
.Content(name: "コンテンツ"), .Content(name: "コンテンツ"), .Content(name: "コンテンツ"),.Content(name: "コンテンツ") | |
]] | |
} | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
} | |
// MARK:- MyViewControlelrDelegate | |
func myViewController(vc: MyViewController<ViewController>, dequeue: ((identifier: String) -> UICollectionViewCell?), cellFor item: Item) -> UICollectionViewCell? { | |
let cell = dequeue(identifier: "cell") | |
switch item { | |
case .Banner(_): | |
cell?.backgroundColor = .redColor() | |
case .Content(_): | |
cell?.backgroundColor = .whiteColor() | |
} | |
return cell | |
} | |
func myViewController(vc: MyViewController<ViewController>, layout collectionViewLayout: UICollectionViewLayout, sizeFor item: Item) -> CGSize? { | |
let contentLength = Int(vc.view.frame.width) / 2 | |
switch item { | |
case .Banner(_): | |
return CGSize(width: vc.view.frame.width, height: 60) | |
case .Content(_): | |
return CGSize(width: contentLength, height: contentLength) | |
} | |
} | |
} | |
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true | |
XCPlaygroundPage.currentPage.liveView = UINavigationController(rootViewController: ViewController(nibName: nil, bundle: nil)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment