我需要构建一个具有异构数据源的UITableView,例如两个模型: person和city。
一种选择是将person数组和城市数组组合成一个AnyObject数组,并检查存储在单元格中的对象类型。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let someobject = self.events[indexPath.row]
var test = self.events
if someobject is Person {
print ("I am a person")
}
if someobject is City {
print ("I am a city")
}
} 另一种选择是创建一个基类,并使Person和City继承Base:
class Person: Base {}
class City: Base {}然后,对tableView数据源使用Base而不是AnyObject。
我想知道是否有更好的方法来做这件事?
发布于 2016-05-29 11:39:43
我会这样做。您有自己的模型类:
class Person {
// ...
}
class City {
// ...
}我假设您的数据是Person和City的混合序列,序列中的每个元素可以是Person或City。
假设每个模型都有一个UITableViewCell子类,如下所示:
class PersonCell: UITableViewCell {
var person: Person? {
didSet {
guard let person = person else { return }
// update appearance using properties of person
}
}
}
class CityCell: UITableViewCell {
var city: City? {
didSet {
guard let city = city else { return }
// update appearance using properties of city
}
}
}我假设您已经在表视图中注册了每种单元格类型,或者通过在情节提要中创建单元格作为原型,或者通过在表视图上调用registerNib:forCellReuseIdentifier:或registerClass:forCellReuseIdentifier:。
此时,您需要的是一种方法,将Person对象和City对象放入数组中,为每个数据对象获取适当类型的单元格,并使用数据对象配置该单元格。让我们为此制定一个协议:
protocol TableViewDatum: class {
/// Return a fully-configured cell for displaying myself in the table view.
func cell(inTableView tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> UITableViewCell
}然后我们扩展Person以符合协议:
extension Person: TableViewDatum {
func cell(inTableView tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Person", forIndexPath: indexPath) as! PersonCell
cell.person = self
return cell
}
}并且我们扩展了City以符合该协议:
extension City: TableViewDatum {
func cell(inTableView tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("City", forIndexPath: indexPath) as! CityCell
cell.city = self
return cell
}
}此时,实现所需的UITableViewDataSource方法是微不足道的:
class MyTableViewDataSource: NSObject, UITableViewDataSource {
private var data = [TableViewDatum]()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return data[indexPath.row].cell(inTableView: tableView, forIndexPath: indexPath)
}
}注1。
您可以使用泛型减少Person扩展和City扩展之间的代码重复,但这可能不值得。它肯定会更难理解,也不会节省太多代码。
注2。
如果您没有自定义单元格类,例如Person,并且您只使用UITableViewCellStyle.Subtitle等标准单元格样式之一,那么您仍然可以使用此模式。您只需在Person扩展中配置单元,而不是在自定义单元类中。示例:
extension Person: TableViewDatum {
func cell(inTableView tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Person", forIndexPath: indexPath)
// Assume cell style is .Subtitle.
cell.textLabel?.text = self.name
cell.detailTextLabel?.text = self.title
return cell
}
}注3。
为了回应评论者NRitH的担忧,我通过“让Person和City模型类负责出售他们的表视图单元格”来“混合模型和视图”:我没有混合模型和视图。使Person和City符合TableViewDatum并提供其单元的扩展与Person和City类定义完全分开。
很容易想象Person和City类来自于某个第三方框架。例如,将Person替换为CNContact (来自iOS联系人框架),将City替换为CLLocation (来自iOS CoreLocations框架)。
此模式在该场景中同样有效:框架提供的类实现对我的应用程序的用户界面一无所知。声称类实现是在出售它们的单元是不合理的。是分机在售卖细胞。
有关此实现风格的更多信息,请查看WWDC 2015 Session 408: Protocol-Oriented Programming in Swift。
关于“对于细胞来说,使用传递给它们的Person或City对象来配置自己会更有意义”,这正是我所做的!我设置了PersonCell的person属性以使PersonCell配置自身,并设置了CityCell的city属性以使CityCell配置自身。
假设您只是想消除通过模型对象本身的任何传递。但正如我所说的,扩展与模型对象是完全分开的,所以我不认为它们有问题。
不过,让我们看看你的方式是什么样子的。我认为你的意思是:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch data[indexPath.row] {
case let person as Person:
let cell = tableView.dequeueReusableCellWithIdentifier("Person", forIndexPath: indexPath) as! PersonCell
cell.person = person
return cell
case let city as City:
let cell = tableView.dequeueReusableCellWithIdentifier("City", forIndexPath: indexPath) as! CityCell
cell.city = city
return cell
default: fatalError()
}
}您有一条switch语句。(如果愿意,您可以使用级联的if语句,但这是相同的基本模式。)如果您实现任何其他特定于行的UITableViewDataSource或UITableViewDelegate方法(例如tableView:canEditRowAtIndexPath:、tableView:editActionsForRowAtIndexPath:等),则可能需要在每个方法中使用类似的switch语句。您是否添加了一个新的模型类,但忘记更新其中一个委托方法?哦,你要等到运行的时候才会知道。在我的设计中,您向TableViewDatum协议添加相应的必需方法,数据源或委托方法将调用该协议方法。如果您忘记在某个模型类中实现该方法,则编译器会标记一个错误。
这也是为什么我的设计没有任何与default: fatalError()案例相对应的东西。编译器强制的类型安全在编译时将其排除。
发布于 2016-05-29 11:30:44
你可以做几件事:
[AnyObject],然后让您的cellForRowAtIndexPath执行以下操作:如果让城市=对象为,让object = dataindexpath.row?城市{//做一些其他的事情}如果让...
https://stackoverflow.com/questions/37505846
复制相似问题