P9CollectionViewHandler
UICollectionView 有助于列出项目。但是,编写控制代码是一种 boilerplate。P9CollectionViewHandler 库帮助您轻松简单地处理 UICollectionView。
安装
您可以从我们的发行页面下载最新的框架文件。P9CollectionViewHandler 也可通过 CocoaPods 获取。要安装它,只需将以下行添加到 Podfile:pod 'P9CollectionViewHandler'
简单预览
let cellIdentifierForType:[String:String] = [
"1" : RedCollectionViewCell.identifier(),
"2" : GreenCollectionViewCell.identifier(),
"3" : BlueCollectionViewCell.identifier()
]
let supplementaryIdentifierForType:[String:String] = [
"1" : HeaderCollectionReusableView.identifier(),
"2" : FooterCollectionReusableView.identifier()
]
let collectionView = UICollectionView(frame .zero)
let handler = P9CollectionViewHandler()
handler.delegate = self
handler.standby(identifier:"sample", cellIdentifierForType: cellIdentifierForType, supplementaryIdentifierForType: supplementaryIdentifierForType, collectionView: collectionView)
var records:[P9CollectionViewHandler.Record] = []
records.append(P9CollectionViewHandler.Record(type: "2", data: nil, extra: nil))
records.append(P9CollectionViewHandler.Record(type: "3", data: nil, extra: nil))
handler.sections.append(P9CollectionViewHandler.Section(headerType: "1", headerData: nil, footerType: nil, footerData: nil, records: records, extra: nil))
collectionView.reloadData()
func collectionViewHandlerCellDidSelect(handlerIdentifier: String, cellIdentifier: String, indexPath: IndexPath, data: Any?, extra: Any?) {
// handling collectionview default select action
}
func collectionViewHandlerCellEvent(handlerIdentifier: String, cellIdentifier: String, eventIdentifier: String?, indexPath:IndexPath?, data: Any?, extra: Any?) {
// handling custom event from cell
}
让我们一个个来看看。
使您的 collectionview cell 确认协议
为了使用 P9CollectionViewHandler,您的 collectionview cell 需要确认并实现以下 P9CollectionViewCellProtocol。如果您想要使用辅助视图,则还需要为它确认和实现相同的协议。
protocol P9CollectionViewCellProtocol: class {
static func identifier() -> String
static func instanceFromNib() -> UIView
static func cellSizeForData(_ data: Any?, extra: Any?) -> CGSize
func setData(_ data: Any?, extra: Any?)
func setDelegate(_ delegate: P9CollectionViewCellDelegate)
func setIndexPath(_ indexPath: IndexPath)
}
identifier 和 instanceFromNib 函数需要返回其标识符字符串和实例对象。但这两个函数是可选的。除非您需要自定义,否则不需要实现。因此,让 identifier 函数返回您的 collection view cell 的类名,并且 instanceFromNib 从标识符返回给定类的实例对象。
cellSizeForData 函数需要返回给定数据对应的 collectionView 单元格的大小。
static func cellSizeForData(_ data: Any?, extra: Any?) -> CGSize {
guard let data = data as? CellDataModel else {
return .zero
}
return CGSize(width: 40, height: 40)
}
setData 函数传递数据和额外对象以更新您的 collectionView 单元格。您可以执行业务代码来更新 collectionView 单元格。
func setData(_ data: Any?, extra: Any?) {
guard let data = data as? CellDataModel else {
return
}
self.data = data
self.titleLabel.text = data.title ?? "Sample"
}
setDelegate 函数传递回调对象以反馈自定义事件。如果您的 collectionView 单元格有自定义事件,首先在控制器中确认 P9CollectionViewCellDelegate。
protocol P9CollectionViewCellDelegate: class {
func collectionViewCellEvent(cellIdentifier:String, eventIdentifier:String?, indexPath:IndexPath?, data:Any?, extra:Any?)
}
extension ViewController: P9CollectionViewCellDelegate {
// ...
}
并通过它设置代理和反馈自定义事件。
func setDelegate(_ delegate: P9CollectionViewCellDelegate) {
self.delegate = delegate
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
delegate?.collectionViewCellEvent(cellIdentifier: SampleCollectionViewCell.identifier(), eventIdentifier: "touch", indexPath: nil, data: data, extra: nil)
}
setIndexPath 函数传递 indexPth 对象。您可以将此 indexPath 信息存储起来,并在事件代理调用时发送。
func setIndexPath(_ indexPath: IndexPath) {
self.indexPath = indexPath
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
delegate?.collectionViewCellEvent(cellIdentifier: SampleCollectionViewCell.identifier(), eventIdentifier: "touch", indexPath: indexPath, data: data, extra: nil)
}
如果您的项目基于 Objective C,那么您需要确认并实现 P9CollectionViewCellObjcProtocol 以支持您的 collectionView 单元格。不是 P9CollectionViewCellProtocol,而是 P9CollectionViewCellObjcProtocol。它具有与 P9CollectionViewCellProtocol 相同的成员函数,但您必须实现所有函数,包括 identifier 和 instanceFromNib。
+ (NSString *)identifier {
return @"SampleCollectionViewCell";
}
+ (UIView *)instanceFromNib {
return [[[NSBundle mainBundle] loadNibNamed:[SampleCollectionViewCell identifier] owner:nil options:nil] firstObject];
}
处理
现在,您的 collectionView 单元格已准备好了。为 collectionView 单元格标识符和补充视图标识符创建字典。只需按您所愿创建唯一的类型键值,或者从服务器响应模型中使用某些类型值。
let cellIdentifierForType:[String:String] = [
"1" : RedCollectionViewCell.identifier(),
"2" : GreenCollectionViewCell.identifier(),
"3" : BlueCollectionViewCell.identifier()
]
let supplementaryIdentifierForType:[String:String] = [
"1" : HeaderCollectionReusableView.identifier(),
"2" : FooterCollectionReusableView.identifier()
]
将它们设置为处理器。别忘了设置代理来从处理器获取反馈。
let handler = P9CollectionViewHandler()
handler.standby(identifier:"sample", cellIdentifierForType: cellIdentifierForType, supplementaryIdentifierForType: supplementaryIdentifierForType, collectionView: collectionView)
handler.delegate = self
并且,您需要为处理器创建模型数据。请放心,您可以使用自己的模型而无需任何更改。只需将它们包装到处理器模型中。以下是处理器模型的定义。
@objc(P9CollectionViewRecord) public class Record : NSObject {
var type:String
var data:Any?
var extra:Any?
@objc public init(type:String, data:Any?, extra:Any?=nil) {
self.type = type
self.data = data
self.extra = extra
}
}
@objc(P9CollectionViewSection) public class Section : NSObject {
var headerType:String?
var headerData:Any?
var footerType:String?
var footerData:Any?
var extra:Any?
var records:[Record]?
@objc public init(headerType:String?, headerData:Any?, footerType:String?, footerData:Any?, records:[Record]?, extra:Any?) {
self.headerType = headerType
self.headerData = headerData
self.footerType = footerType
self.footerData = footerData
self.records = records
self.extra = extra
}
}
您可以根据常规 collectionView 数据结构创建具有 N 个部分和每部分 M 个记录的模型。根据需要创建记录和部分,并将它们设置为处理器。
var records:[P9CollectionViewHandler.Record] = []
records.append(P9CollectionViewHandler.Record(type: "1", data: nil, extra: nil))
records.append(P9CollectionViewHandler.Record(type: "2", data: nil, extra: nil))
handler.sections.append(P9CollectionViewHandler.Section(headerType: nil, headerData: nil, footerType: nil, footerData: nil, records: records, extra: nil))
然后重新加载目标 collectionView。
collectionView.reloadData()
现在,通过确认协议 P9CollectionViewHandlerDelegate 从每个 collectionView 单元格获取消息。以下是协议和示例实现。
@objc public protocol P9CollectionViewHandlerDelegate: class {
@objc optional func collectionViewHandlerWillBeginDragging(handlerIdentifier:String, contentSize:CGSize, contentOffset:CGPoint)
@objc optional func collectionViewHandlerDidScroll(handlerIdentifier:String, contentSize:CGSize, contentOffset:CGPoint)
@objc optional func collectionViewHandlerDidEndScroll(handlerIdentifier:String, contentSize:CGSize, contentOffset:CGPoint)
@objc optional func collectionViewHandler(handlerIdentifier:String, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
@objc optional func collectionViewHandler(handlerIdentifier:String, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)
@objc optional func collectionViewHandler(handlerIdentifier:String, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
@objc optional func collectionViewHandler(handlerIdentifier:String, didEndDisplayingSupplementaryView view: UICollectionReusableView, forElementOfKind elementKind: String, at indexPath: IndexPath)
@objc optional func collectionViewHandler(handlerIdentifier:String, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
@objc optional func collectionViewHandler(handlerIdentifier:String, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat
@objc optional func collectionViewHandler(handlerIdentifier:String, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
@objc optional func collectionViewHandlerCellDidSelect(handlerIdentifier:String, cellIdentifier:String, indexPath:IndexPath, data:Any?, extra:Any?)
@objc optional func collectionViewHandlerCellEvent(handlerIdentifier:String, cellIdentifier:String, eventIdentifier:String?, indexPath:IndexPath?, data:Any?, extra:Any?)
}
extension ViewController: P9CollectionViewHandlerDelegate {
func collectionViewHandlerCellDidSelect(handlerIdentifier: String, cellIdentifier: String, indexPath: IndexPath, data: Any?, extra: Any?) {
print("handler \(handlerIdentifier) cell \(cellIdentifier) indexPath \(indexPath.section):\(indexPath.row) did select")
}
func collectionViewHandlerCellEvent(handlerIdentifier: String, cellIdentifier:String, eventIdentifier:String?, indexPath: IndexPath?, data: Any?, extra: Any?) {
print("handler \(handlerIdentifier) cell \(cellIdentifier) event \(eventIdentifier ?? "")")
}
}
如果您不喜欢庞大的 switch 代码,则可以使用每个事件标识符的回调函数(或块)。
enum EventId: String {
case clickMe
}
handler.registerCallback(callback: doClickMe(indexPath:data:extra:), forCellIdentifier: CollectionViewCell.identifier(), withEventIdentifier: EventId.clickMe.rawValue)
extension TableViewCell {
func doClickMe(indexPath:IndexPath?, data:Any?, extra:Any?) {
print("Got Click Me.")
}
}
您还可以通过不传递事件标识符来使用回调函数(或块)选择单元格事件。
handler.registerCallback(callback: collectionViewCellSelectHandler(indexPath:data:extra:), forCellIdentifier: CollectionViewCell.identifier())
许可
适用于 MIT 许可证。 http://en.wikipedia.org/wiki/MIT_License