从 swift 中的 UICollectionViewDataSource 继承时的通用 class
Generic class when inherit from UICollectionViewDataSource in swift
当我尝试创建一个在 swift 中实现 UICollectionViewDataSource 的通用 class 时,它说我的 class 不符合协议(有时 Xcode 崩溃)。
这是否意味着我们无法为 UICollectionView 创建通用数据提供程序并且我们必须重复代码?
通用代码如下:
// Enum protocol
protocol OptionsEnumProtocol
{
typealias T
static var allValues:[T] {get set}
var description: String {get}
func iconName() -> String
}
// enum : list of first available options
enum Options: String, OptionsEnumProtocol
{
typealias T = Options
case Color = "Color"
case Image = "Image"
case Shadow = "Shadow"
static var allValues:[Options] = [Color, Image, Shadow]
var description: String {
return self.rawValue
}
func iconName() -> String
{
var returnValue = ""
switch(self)
{
case .Color: returnValue = "color_icon"
case .Image: returnValue = "image_icon"
case .Shadow: returnValue = "shadow_icon"
}
return returnValue
}
}
// class to use as the uicollectionview datasource and delegate
class OptionsDataProvider<T>: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
private let items = T.allValues
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
// Configure the cell
cell.iconFileName = item.iconName()
cell.labelView.text = item.description
return cell
}
}
但是因为它失败了,我不得不改用这个非通用形式:
enum Options: String
{
case Color = "Color"
case Image = "Image"
case Shadow = "Shadow"
static var allValues:[Options] = [Color, Image, Shadow]
var description: String {
return self.rawValue
}
func iconName() -> String
{
var returnValue = ""
switch(self)
{
case .Color: returnValue = "color_icon"
case .Image: returnValue = "image_icon"
case .Shadow: returnValue = "shadow_icon"
}
return returnValue
}
}
class OptionsDataProvider: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
private let items = Options.allValues
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
// Configure the cell
cell.iconFileName = item.iconName()
cell.labelView.text = item.description
return cell
}
}
这使我有义务为我拥有的每个枚举类型复制 class。
确切错误:
从协议继承时,您必须实现所有必需的方法。 Swift 2 会有所改变。也许你真的想继承 class.
你是对的,写一个泛型是不可能的class。但是,我找到了解决方法。它不使用枚举,因此您可能不会觉得它很有用。然而,它实现了你想要的——你得到了一个集合视图数据源,它可以与提供必要数据的不同 classes 一起使用。这是代码:
protocol OptionsProviderProtocol
{
func allValues() -> [OptionsItem]
}
class OptionsItem:NSObject {
let itemDescription:String
let iconName:String
init(iconName:String,description:String) {
self.itemDescription = description
self.iconName = iconName
}
}
// class stores first available options
class Options: NSObject, OptionsProviderProtocol
{
let color = OptionsItem(iconName: "color_icon", description: "Color")
let image = OptionsItem(iconName: "image_icon", description: "Image")
let shadow = OptionsItem(iconName: "shadow_icon", description: "Shadow")
func allValues() -> [OptionsItem] {
return [color, image, shadow]
}
}
// class to use as the uicollectionview datasource and delegate
class OptionsDataProvider: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
private var items:[OptionsItem] = []
convenience init(optionsProvider:OptionsProviderProtocol) {
self.items = optionsProvider.allValues()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
// Configure the cell
cell.iconFileName = item.iconName()
cell.labelView.text = item.description
return cell
}
}
如果您有任何问题,请告诉我。
当我试图从 NSOperation class 继承通用 class 时,我有类似的 problem/question。 xCode 没有给我编译错误,因为没有涉及协议,相反,我的 override func main()
根本就没有被调用过:)
无论如何...如果您关注 workaround 先生。 Topal Sergey 建议,您可以相对轻松地实现您想要的目标。
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView?
private var defaultDataProvider = OptionsDataProvider<Options>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collectionView?.delegate = defaultDataProvider
collectionView?.dataSource = defaultDataProvider
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// Enum protocol
protocol OptionsEnumProtocol {
static var allValues: [OptionsEnumProtocol] {get set}
var description: String {get}
func iconName() -> String
}
// enum : list of first available options
enum Options: String, OptionsEnumProtocol {
case Color = "Color"
case Image = "Image"
case Shadow = "Shadow"
static var allValues: [OptionsEnumProtocol] = [Color, Image, Shadow]
var description: String {
return self.rawValue
}
func iconName() -> String
{
var returnValue = ""
switch(self)
{
case .Color: returnValue = "color_icon"
case .Image: returnValue = "image_icon"
case .Shadow: returnValue = "shadow_icon"
}
return returnValue
}
}
class OptionsDataProviderWrapper: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// MARK: protocols' funcs
final func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return wrapperCollectionView(collectionView, numberOfItemsInSection: section)
}
final func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return wrapperCollectionView(collectionView, cellForItemAtIndexPath: indexPath)
}
// MARK: for override
func wrapperCollectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
func wrapperCollectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
}
class OptionsDataProvider<T: OptionsEnumProtocol>: OptionsDataProviderWrapper {
private let items = T.allValues
override func wrapperCollectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
override func wrapperCollectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("reuseId", forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
cell.labelView?.text = item.description
return cell
}
}
class GenericIconLabelCell: UICollectionViewCell {
@IBOutlet weak var labelView: UILabel?
}
这里的关键是创建 OptionsDataProviderWrapper
不是通用的并实现您的所有协议。它所做的唯一一件事 - 它将调用重定向到另一个函数,如 func wrapperCollectionView...
现在您可以从此 OptionsDataProviderWrapper
继承您的通用 class 并覆盖该包装函数。
注意:您必须完全覆盖包装函数,因为本机 func collectionView...
函数不会在您的通用子 class 中调用,这与我的 NSOperation 问题类似。这就是为什么我用 final
.
标记原生函数
当我尝试创建一个在 swift 中实现 UICollectionViewDataSource 的通用 class 时,它说我的 class 不符合协议(有时 Xcode 崩溃)。
这是否意味着我们无法为 UICollectionView 创建通用数据提供程序并且我们必须重复代码?
通用代码如下:
// Enum protocol
protocol OptionsEnumProtocol
{
typealias T
static var allValues:[T] {get set}
var description: String {get}
func iconName() -> String
}
// enum : list of first available options
enum Options: String, OptionsEnumProtocol
{
typealias T = Options
case Color = "Color"
case Image = "Image"
case Shadow = "Shadow"
static var allValues:[Options] = [Color, Image, Shadow]
var description: String {
return self.rawValue
}
func iconName() -> String
{
var returnValue = ""
switch(self)
{
case .Color: returnValue = "color_icon"
case .Image: returnValue = "image_icon"
case .Shadow: returnValue = "shadow_icon"
}
return returnValue
}
}
// class to use as the uicollectionview datasource and delegate
class OptionsDataProvider<T>: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
private let items = T.allValues
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
// Configure the cell
cell.iconFileName = item.iconName()
cell.labelView.text = item.description
return cell
}
}
但是因为它失败了,我不得不改用这个非通用形式:
enum Options: String
{
case Color = "Color"
case Image = "Image"
case Shadow = "Shadow"
static var allValues:[Options] = [Color, Image, Shadow]
var description: String {
return self.rawValue
}
func iconName() -> String
{
var returnValue = ""
switch(self)
{
case .Color: returnValue = "color_icon"
case .Image: returnValue = "image_icon"
case .Shadow: returnValue = "shadow_icon"
}
return returnValue
}
}
class OptionsDataProvider: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
private let items = Options.allValues
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
// Configure the cell
cell.iconFileName = item.iconName()
cell.labelView.text = item.description
return cell
}
}
这使我有义务为我拥有的每个枚举类型复制 class。
确切错误:
从协议继承时,您必须实现所有必需的方法。 Swift 2 会有所改变。也许你真的想继承 class.
你是对的,写一个泛型是不可能的class。但是,我找到了解决方法。它不使用枚举,因此您可能不会觉得它很有用。然而,它实现了你想要的——你得到了一个集合视图数据源,它可以与提供必要数据的不同 classes 一起使用。这是代码:
protocol OptionsProviderProtocol
{
func allValues() -> [OptionsItem]
}
class OptionsItem:NSObject {
let itemDescription:String
let iconName:String
init(iconName:String,description:String) {
self.itemDescription = description
self.iconName = iconName
}
}
// class stores first available options
class Options: NSObject, OptionsProviderProtocol
{
let color = OptionsItem(iconName: "color_icon", description: "Color")
let image = OptionsItem(iconName: "image_icon", description: "Image")
let shadow = OptionsItem(iconName: "shadow_icon", description: "Shadow")
func allValues() -> [OptionsItem] {
return [color, image, shadow]
}
}
// class to use as the uicollectionview datasource and delegate
class OptionsDataProvider: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
private var items:[OptionsItem] = []
convenience init(optionsProvider:OptionsProviderProtocol) {
self.items = optionsProvider.allValues()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
// Configure the cell
cell.iconFileName = item.iconName()
cell.labelView.text = item.description
return cell
}
}
如果您有任何问题,请告诉我。
当我试图从 NSOperation class 继承通用 class 时,我有类似的 problem/question。 xCode 没有给我编译错误,因为没有涉及协议,相反,我的 override func main()
根本就没有被调用过:)
无论如何...如果您关注 workaround 先生。 Topal Sergey 建议,您可以相对轻松地实现您想要的目标。
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView?
private var defaultDataProvider = OptionsDataProvider<Options>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collectionView?.delegate = defaultDataProvider
collectionView?.dataSource = defaultDataProvider
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// Enum protocol
protocol OptionsEnumProtocol {
static var allValues: [OptionsEnumProtocol] {get set}
var description: String {get}
func iconName() -> String
}
// enum : list of first available options
enum Options: String, OptionsEnumProtocol {
case Color = "Color"
case Image = "Image"
case Shadow = "Shadow"
static var allValues: [OptionsEnumProtocol] = [Color, Image, Shadow]
var description: String {
return self.rawValue
}
func iconName() -> String
{
var returnValue = ""
switch(self)
{
case .Color: returnValue = "color_icon"
case .Image: returnValue = "image_icon"
case .Shadow: returnValue = "shadow_icon"
}
return returnValue
}
}
class OptionsDataProviderWrapper: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// MARK: protocols' funcs
final func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return wrapperCollectionView(collectionView, numberOfItemsInSection: section)
}
final func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return wrapperCollectionView(collectionView, cellForItemAtIndexPath: indexPath)
}
// MARK: for override
func wrapperCollectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
func wrapperCollectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
}
class OptionsDataProvider<T: OptionsEnumProtocol>: OptionsDataProviderWrapper {
private let items = T.allValues
override func wrapperCollectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
override func wrapperCollectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("reuseId", forIndexPath: indexPath) as! GenericIconLabelCell
let item = self.items[indexPath.row]
cell.labelView?.text = item.description
return cell
}
}
class GenericIconLabelCell: UICollectionViewCell {
@IBOutlet weak var labelView: UILabel?
}
这里的关键是创建 OptionsDataProviderWrapper
不是通用的并实现您的所有协议。它所做的唯一一件事 - 它将调用重定向到另一个函数,如 func wrapperCollectionView...
现在您可以从此 OptionsDataProviderWrapper
继承您的通用 class 并覆盖该包装函数。
注意:您必须完全覆盖包装函数,因为本机 func collectionView...
函数不会在您的通用子 class 中调用,这与我的 NSOperation 问题类似。这就是为什么我用 final
.