Swift,“子类" UITableView的数据源方法以某种方式? [英] Swift, "subclass" the data source methods of UITableView somehow?

查看:77
本文介绍了Swift,“子类" UITableView的数据源方法以某种方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一个表视图控制器ExtraRowTableViewController

Imagine a table view controller ExtraRowTableViewController,

总是在第三行之后(例如)插入一个额外的行.

which always inserts an extra row, after (let's say) the third row.

因此在此示例中...

So in this example ...

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.

ExtraRowTableViewController would "take over" and actually return 51.

对于cellForRowAtIndexPath,它将接管"并在第四行返回其自己的单元格,它将从0到3返回您的单元格第N行,对于高于4的行将返回您的单元格行减一.

For cellForRowAtIndexPath, it would "take over" and return its own cell at row four, it would return your cell row N from 0 to 3, and it would return your cell row minus one for rows above four.

如何在ExtraRowTableViewController中实现?

因此SomeList的程序员根本不需要进行任何更改.

So that the programmer of SomeList need make no change at all.

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

Would you be subclassing UITableView, or the data source delegate .. or??

为澄清起见,示例用例可能是在第四行添加广告,编辑字段或一些特殊新闻. SomeList的程序员完全不需要采取任何措施来实现此目标,即以完全面向对象的方式实现.

To clarify, an example use case might be, let's say, adding an ad, editing field, or some special news, at the fourth row. It would be appropriate that the programmer of SomeList need do absolutely nothing to achieve this, ie it is achieved in a completely OO manner.

请注意,当然,添加新的替换"调用很容易,您的表视图将只知道"使用这些调用即可代替普通调用. (RMenke在下面提供了一个有用的完整示例.)因此,

Note that it's, of course, easy to just add new "substitute" calls, which your table view would "just know" to use instead of the normal calls. (RMenke has provide a useful full example of this below.) So,

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解决方案.

In the example your table view programmer would have to "just know" that they must use specialNumberOfRowsInSection and specialCellForRowAtIndexPath in SpecialTableViewController rather than the usual calls ... it's not a clean, drop-in, OO solution.

注意:我很高兴您可以以某种方式子类化NSObject来覆盖信号(例如在此处),但是不是语言解决方案.

Note: I appreciate you could probably subclass NSObject in some way to override the signals (such as discussed here), but that is not a language solution.

推荐答案

github链接->可能包含更多更新的代码

github link -> might contain more updated code

要回答的问题:不可能以子类的形式覆盖UITableViewController和UITableViewDataSource之间的函数的标准流程.

To answer the question: It is not possible to override the standard flow of the functions between the UITableViewController and the UITableViewDataSource in the form of a subclass.

UIKit源代码就像一个大黑盒子,我们无法看到或更改. (如果您这样做,则将拒绝应用程序.)要完全执行所需的操作,您将需要覆盖从UITableViewDataSource调用这些函数的函数,以便它们指向第三个函数,而不是协议函数.第三个功能将改变基本行为,并从UITableViewDataSource触发该功能.这样,对于其他开发人员来说,一切都将保持不变.

The UIKit source code is like a big black box which we can not see or alter. (apps will be rejected if you do.) To do exactly what you want you would need to override the functions that call on the functions from the UITableViewDataSource so they point to a third function instead of to the protocol functions. This third function would alter the basic behaviour and trigger the function from the UITableViewDataSource. This way it would all stay the same for other devs.

Hack :子类化整个UITableviewController->您需要存储的属性.这样,其他人可以继承您的自定义类,而他们在引擎盖下看不到任何魔幻/混乱.

Hack : Subclass the entire UITableviewController -> you need stored properties. This way other people can subclass your custom class and they won't see any of the magic/mess under the hood.

下面的类使用与常规UITableViewController相同的样式.用户将覆盖他们希望更改的方法.由于这些方法是在现有函数中使用的,因此您可以更改功能.

The class below uses the same style as the regular UITableViewController. Users override the methods they wish to alter. Because those methods are used inside the existing function you get an altered functionality.

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

Unfortunately it is not possible to mark those functions as private.

indexPath的适配器存储Bool和原始indexPath. ->这将与您的数据相对应.

The adapter for the indexPath stores a Bool and the original indexPath. -> This will correspond to your data.

新插入的单元格将基于在其创建的部分获得一个indexPath以及一个计数器. ->可能有用.

The new inserted cells will get an indexPath based on the section they are created in and a counter. -> Could be useful.

更新:在y行之后添加x多余的行

Update: Add x extra rows after y rows

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)
        }
    }
}

这篇关于Swift,“子类" UITableView的数据源方法以某种方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆