我需要构建一个具有异构数据源的UITableView示例两个模型:person和city。
一种方法是将person数组和city数组组合成AnyObject数组,并检查存储在cell中的对象类型。
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")
}
}
另一个选项是创建基类并使人和城市从基类继承:
class Person: Base {}
class City: Base {}
然后用[Base]代替[AnyObject]作为tableView数据源。
我想知道是否有更好的方法来做这件事?
最佳答案
我会这样做的。你有你的模特课程:
class Person {
// ...
}
class City {
// ...
}
我假设您的数据是
Person
和City
的混合序列,其中序列中的每个元素可能是aPerson
或aCity
。假设每个模型都有一个子类,如下所示:
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
}
}
}
我假设您已经在table视图中注册了每个单元格类型,可以在脚本中创建作为原型的单元格,也可以在table视图中调用
UITableViewCell
或registerNib:forCellReuseIdentifier:
。此时需要的是将
registerClass:forCellReuseIdentifier:
对象和Person
对象放在一个数组中,为每个数据对象获取适当类型的单元格,并用数据对象配置单元格。我们来制定一个协议:protocol TableViewDatum: class {
/// Return a fully-configured cell for displaying myself in the table view.
func cell(inTableView tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> UITableViewCell
}
然后我们扩展
City
以符合协议: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
}
}
我们将
Person
扩展到符合协议: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
}
}
此时,实现所需的
City
方法很简单: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。
使用泛型可以减少
UITableViewDataSource
扩展和Person
扩展之间的代码重复,但这可能不值得。这肯定会更难理解,也不会节省太多代码。注2。
如果您没有自定义的单元格类,比如说,
City
,而您只是使用了一种标准的单元格样式,比如Person
,那么您仍然可以使用此模式。您只需在UITableViewCellStyle.Subtitle
扩展名中而不是在自定义单元格类中配置单元格。例子: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关心的是,我正在“混合模型和观点”,通过“[H]让人和城市模型类负责出售他们的表格视图单元格”:我没有混合模型和视图。使
Person
和Person
符合City
的扩展,因此它们的单元是与TableViewDatum
和Person
类定义完全分离的。很容易想象
City
和Person
类来自某个第三方框架。例如,将City
替换为Person
(来自iOS联系人框架)并将CNContact
替换为City
(来自iOS核心位置框架)。这种模式在这种情况下也同样有效:框架提供的类实现对我的应用程序的用户界面一无所知。声称类实现正在出售其单元是不合理的。是分机在卖电池。
有关此实现样式的更多信息,请查看WWDC 2015 Session 408: Protocol-Oriented Programming in Swift。
关于“让细胞用传递给他们的人或城市对象来配置自己更有意义”,这正是我所做的!我将
CLLocation
的person
属性设置为使PersonCell
自行配置,并将PersonCell
的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语句。(如果您愿意,可以使用
CityCell
语句的级联,但它是相同的基本模式。)如果您实现任何其他特定于行的if
或UITableViewDataSource
方法(例如UITableViewDelegate
、tableView:canEditRowAtIndexPath:
等),则可能需要在每个方法中使用类似的switch语句。是否添加了新的模型类,但忘记更新其中一个委托方法?哦,你要等到运行时才能知道。在我的设计中,向tableView:editActionsForRowAtIndexPath:
协议添加相应的必需方法,数据源或委托方法调用该协议方法。如果忘记在某个模型类中实现该方法,编译器将标记错误。这也是为什么我的设计没有任何与
TableViewDatum
案例对应的东西。编译器强制类型安全在编译时将其排除。
关于ios - 如何在UITableViewController中分配异构数据源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37505846/