首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在UITableViewController中分配异构数据源?

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

Stack Overflow用户
提问于 2016-05-29 11:22:33
回答 2查看 161关注 0票数 0

我需要构建一个具有异构数据源的UITableView,例如两个模型: person和city。

一种选择是将person数组和城市数组组合成一个AnyObject数组,并检查存储在单元格中的对象类型。

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
 class Person: Base {}
 class City: Base {}

然后,对tableView数据源使用Base而不是AnyObject。

我想知道是否有更好的方法来做这件事?

EN

回答 2

Stack Overflow用户

发布于 2016-05-29 11:39:43

我会这样做。您有自己的模型类:

代码语言:javascript
运行
复制
class Person {
    // ...
}

class City {
    // ...
}

我假设您的数据是PersonCity的混合序列,序列中的每个元素可以是PersonCity

假设每个模型都有一个UITableViewCell子类,如下所示:

代码语言:javascript
运行
复制
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对象放入数组中,为每个数据对象获取适当类型的单元格,并使用数据对象配置该单元格。让我们为此制定一个协议:

代码语言:javascript
运行
复制
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以符合协议:

代码语言:javascript
运行
复制
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以符合该协议:

代码语言:javascript
运行
复制
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方法是微不足道的:

代码语言:javascript
运行
复制
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扩展中配置单元,而不是在自定义单元类中。示例:

代码语言:javascript
运行
复制
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模型类负责出售他们的表视图单元格”来“混合模型和视图”:我没有混合模型和视图。使PersonCity符合TableViewDatum并提供其单元的扩展与PersonCity类定义完全分开。

很容易想象PersonCity类来自于某个第三方框架。例如,将Person替换为CNContact (来自iOS联系人框架),将City替换为CLLocation (来自iOS CoreLocations框架)。

此模式在该场景中同样有效:框架提供的类实现对我的应用程序的用户界面一无所知。声称类实现是在出售它们的单元是不合理的。是分机在售卖细胞。

有关此实现风格的更多信息,请查看WWDC 2015 Session 408: Protocol-Oriented Programming in Swift

关于“对于细胞来说,使用传递给它们的Person或City对象来配置自己会更有意义”,这正是我所做的!我设置了PersonCellperson属性以使PersonCell配置自身,并设置了CityCellcity属性以使CityCell配置自身。

假设您只是想消除通过模型对象本身的任何传递。但正如我所说的,扩展与模型对象是完全分开的,所以我不认为它们有问题。

不过,让我们看看你的方式是什么样子的。我认为你的意思是:

代码语言:javascript
运行
复制
    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语句,但这是相同的基本模式。)如果您实现任何其他特定于行的UITableViewDataSourceUITableViewDelegate方法(例如tableView:canEditRowAtIndexPath:tableView:editActionsForRowAtIndexPath:等),则可能需要在每个方法中使用类似的switch语句。您是否添加了一个新的模型类,但忘记更新其中一个委托方法?哦,你要等到运行的时候才会知道。在我的设计中,您向TableViewDatum协议添加相应的必需方法,数据源或委托方法将调用该协议方法。如果您忘记在某个模型类中实现该方法,则编译器会标记一个错误。

这也是为什么我的设计没有任何与default: fatalError()案例相对应的东西。编译器强制的类型安全在编译时将其排除。

票数 2
EN

Stack Overflow用户

发布于 2016-05-29 11:30:44

你可以做几件事:

  1. 制定了一个协议,并让它们都符合它,这样你就不会有超类问题,并且仍然具有相同的类型。
  2. 只需执行[AnyObject],然后让您的cellForRowAtIndexPath执行以下操作:

如果让城市=对象为,让object = dataindexpath.row?城市{//做一些其他的事情}如果让...

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37505846

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档