ios - 如何在UITableViewController中分配异构数据源?

标签 ios swift uitableview

我需要构建一个具有异构数据源的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 {
    // ...
}

我假设您的数据是PersonCity的混合序列,其中序列中的每个元素可能是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视图中调用UITableViewCellregisterNib: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]让人和城市模型类负责出售他们的表格视图单元格”:我没有混合模型和视图。使PersonPerson符合City的扩展,因此它们的单元是与TableViewDatumPerson类定义完全分离的。
很容易想象CityPerson类来自某个第三方框架。例如,将City替换为Person(来自iOS联系人框架)并将CNContact替换为City(来自iOS核心位置框架)。
这种模式在这种情况下也同样有效:框架提供的类实现对我的应用程序的用户界面一无所知。声称类实现正在出售其单元是不合理的。是分机在卖电池。
有关此实现样式的更多信息,请查看WWDC 2015 Session 408: Protocol-Oriented Programming in Swift
关于“让细胞用传递给他们的人或城市对象来配置自己更有意义”,这正是我所做的!我将CLLocationperson属性设置为使PersonCell自行配置,并将PersonCellcity属性设置为使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语句的级联,但它是相同的基本模式。)如果您实现任何其他特定于行的ifUITableViewDataSource方法(例如UITableViewDelegatetableView:canEditRowAtIndexPath:等),则可能需要在每个方法中使用类似的switch语句。是否添加了新的模型类,但忘记更新其中一个委托方法?哦,你要等到运行时才能知道。在我的设计中,向tableView:editActionsForRowAtIndexPath:协议添加相应的必需方法,数据源或委托方法调用该协议方法。如果忘记在某个模型类中实现该方法,编译器将标记错误。
这也是为什么我的设计没有任何与TableViewDatum案例对应的东西。编译器强制类型安全在编译时将其排除。

关于ios - 如何在UITableViewController中分配异构数据源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37505846/

相关文章:

ios - RxSwift - 停止重复订阅观察者

iphone - UINavigationController:推自己?

ios - UIActivityViewController 和自定义应用程序

ios - 使用 Bitcode 构建一个 iOS 库,以便与 XCode 6 向后兼容。怎么做?

ios - 通过代码设置 WKInterfaceImage 的(边界)半径

ios - IndexPath.row 不是静态单元格唯一的

ios - 如何在 UITableView 部分之间添加分隔符?

ios - 如何知道您的短信过滤器扩展是否在 iOS 设置中启用?

iphone - SQLite 问题 : sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) 不工作

ios - Swift/Parse - 无法使用类型为 'getObjectInBackgroundWithId' 的参数列表调用 '(PFUser?, (PFObject?, NSError?) -> Void)'