Swift,以某种方式“子类化”UITableView的数据源方法?

时间:2015-09-26 14:02:15

标签: swift oop override subclass

想象一下表视图控制器ExtraRowTableViewController

总是在(比方说)第三行之后插入一个额外的行。

所以在这个例子中......

class SomeList:ExtraRowTableViewController
override func numberOfSectionsInTableView(tableView: UITableView)->Int
    {
    return yourData.count ... say, 50 items
    }
override func tableView
    (tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath)
                                        -> UITableViewCell
    {
    return yourData.cell ... for that row number
    }

ExtraRowTableViewController将“接管”并实际返回51.

对于cellForRowAtIndexPath,它将“接管”并在第四行返回自己的单元格,它会将您的单元格行N从0返回到3,并且对于超过四行的行,它将返回您的单元格行减一。

如何在ExtraRowTableViewController

中实现这一目标

所以SomeList的程序员根本不需要做任何修改。

您是继承UITableView,还是数据源委托..或??

为了澄清,一个示例用例可能是,在第四行添加广告,编辑字段或一些特殊新闻。 SomeList的程序员需要完全没有做到这一点,即完全以OO方式实现。

请注意,当然,添加新的“替代”调用很容易,您的表视图将“只知道”使用而不是正常调用。 (RMenke提供了以下有用的完整示例。)所以,

class SpecialTableViewController:UITableViewController

func tableView(tableView: UITableView, specialNumberOfRowsInSection section: Int) -> Int
  {
  print ("You forgot to supply an override for specialNumberOfRowsInSection")
  }

func tableView
  (tableView:UITableView, specialCellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell
  {
  print ("You forgot to supply an override for specialCellForRowAtIndexPath")
  }

override final func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
  {
  return self.specialNumberOfRowsInSection(section) + 1
  }

override final func tableView
  (tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell
  {
  if indexPath.row == 4
    { return ... the special advertisement cell ... }
  if indexPath.row < 4
    { return self.specialCellForRowAtIndexPath( indexPath )
  if indexPath.row > 4
    { return self.specialCellForRowAtIndexPath( [indexPath.row - 1] )
  }

在示例中,您的表视图程序员必须“只知道”他们必须在SpecialTableViewController中使用specialNumberOfRowsInSection和specialCellForRowAtIndexPath而不是通常的调用......它不是一个干净的,插入式的OO解决方案。

注意:我感谢您可能以某种方式继承NSObject以覆盖信号(例如讨论过here),但这不是语言解决方案。

1 个答案:

答案 0 :(得分:3)

github link - &gt;可能包含更多更新的代码

回答问题:无法以子类的形式覆盖UITableViewController和UITableViewDataSource之间的标准函数流。

UIKit源代码就像一个我们无法看到或改变的大黑盒子。 (如果你这样做,应用程序将被拒绝。)要完全按照你想要的方式做,你需要覆盖从UITableViewDataSource调用函数的函数,这样它们就指向第三个函数而不是协议函数。第三个函数将改变基本行为并从UITableViewDataSource触发函数。这样,对于其他开发者来说,它们都会保持不变。

Hack :对整个UITableviewController进行子类化 - &gt;你需要存储的属性。通过这种方式,其他人可以将您的自定义类子类化,并且他们不会看到任何魔术/混乱。

下面的类使用与常规UITableViewController相同的样式。用户覆盖他们希望改变的方法。因为这些方法在现有函数中使用,所以您可以获得更改的功能。

不幸的是,无法将这些功能标记为私有。

indexPath的适配器存储Bool和原始indexPath。 - &GT;这将与您的数据相对应。

新插入的单元格将根据创建的部分和计数器获取indexPath。 - &GT;可能有用。

更新:在y行之后添加x个额外行

class IATableViewController: UITableViewController {

    private var adapters : [[cellAdapter]] = []

    private struct cellAdapter {
        var isDataCell : Bool = true
        var indexPath : NSIndexPath = NSIndexPath()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func cellIdentifier(tableView: UITableView, isDataCell: Bool) -> String {
        return "Cell"
    }


    func numberOfSections(tableView: UITableView) -> Int {
        return 0
    }

    func numberOfRowsInSection(tableView: UITableView, section: Int) -> Int {
        return 0
    }

    func insertXRowsEveryYRows(tableView: UITableView, section: Int) -> (numberOfRows:Int, everyYRows:Int)? {
        //(numberOfRows:0, everyYRows:0)
        return nil
    }

    func insertXRowsAfterYRows(tableView: UITableView, section: Int) -> (numberOfRows:Int, afterYRows:Int)? {
        //(numberOfRows:0, afterYRows:0)
        return nil
    }

    internal override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        let sections = numberOfSections(tableView)

        adapters = []

        for _ in 0..<sections {
            adapters.append([])
        }

        return sections
    }

    internal override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        let rows = numberOfRowsInSection(tableView, section: section)

        adapters[section] = []

        for row in 0..<rows {
            var adapter = cellAdapter()
            adapter.indexPath = NSIndexPath(forRow: row, inSection: section)
            adapter.isDataCell = true
            adapters[section].append(adapter)
        }

        insertion(tableView, section: section)

        return adapters[section].count
    }

    private func insertion(tableView: UITableView, section: Int) {

        if let insertRowEvery = insertXRowsEveryYRows(tableView, section: section) {
            let insertionPoint = insertRowEvery.everyYRows
            let insertionTimes = insertRowEvery.numberOfRows

            var counter = 0

            var startArray = adapters[section]
            var insertionArray: [cellAdapter] = []

            while !startArray.isEmpty {

                if startArray.count > (insertionPoint - 1) {

                    for _ in 0..<insertionPoint {
                        insertionArray.append(startArray.removeFirst())
                    }
                    for _ in 0..<insertionTimes {
                        var adapter = cellAdapter()
                        adapter.indexPath = NSIndexPath(forRow: counter, inSection: section)
                        adapter.isDataCell = false
                        insertionArray.append(adapter)
                        counter += 1
                    }
                } else {
                    insertionArray += startArray
                    startArray = []
                }
            }

            adapters[section] = insertionArray

        }
        else if let insertRowAfter = insertXRowsAfterYRows(tableView, section: section) {

            let insertionPoint = insertRowAfter.afterYRows
            let insertionTimes = insertRowAfter.numberOfRows

            if adapters[section].count > (insertionPoint - 1) {

                for i in 0..<insertionTimes {

                    var adapter = cellAdapter()
                    adapter.indexPath = NSIndexPath(forRow: i, inSection: section)
                    adapter.isDataCell = false
                    adapters[section].insert(adapter, atIndex: insertionPoint)

                }
            }
        }
    }


    func insertionCellForRowAtIndexPath(tableView: UITableView, cell: UITableViewCell, indexPath: NSIndexPath) -> UITableViewCell {

        return cell
    }



    func dataCellForRowAtIndexPath(tableView: UITableView, cell: UITableViewCell, indexPath: NSIndexPath) -> UITableViewCell {

        return cell

    }


    internal override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let adapter = adapters[indexPath.section][indexPath.row]

        let identifier = cellIdentifier(tableView, isDataCell: adapter.isDataCell)
        let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)

        switch adapter.isDataCell {
        case true :
            return dataCellForRowAtIndexPath(tableView, cell: cell, indexPath: adapter.indexPath)
        case false :
            return insertionCellForRowAtIndexPath(tableView, cell: cell, indexPath: adapter.indexPath)
        }
    }
}