Swift, "subclass" UITableView 的数据源方法不知何故?
Swift, "subclass" the data source methods of UITableView somehow?
想象一个 table 视图控制器 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
会 "take over" 而实际上 return 51.
对于 cellForRowAtIndexPath,它会 "take over" 和 return 它自己的第四行单元格,它会 return 你的单元格行 N 从 0 到 3,它会 return 四行以上的单元格行减一。
如何在 ExtraRowTableViewController
中实现?
所以SomeList的程序员根本不需要做任何改变。
您会子类化 UITableView 或数据源委托 .. 还是??
为了澄清,一个示例用例可能是,比方说,在第四行添加广告、编辑字段或一些特殊新闻。 SomeList 的程序员不需要做任何事情来实现这一点是合适的,即它是以完全面向对象的方式实现的。
请注意,当然,添加新的 "substitute" 调用很容易,您的 table 视图将 "just know" 使用这些调用来代替普通调用。 (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] )
}
在示例中,您的 table 视图程序员必须 "just know" 他们必须在 SpecialTableViewController 中使用 specialNumberOfRowsInSection 和 specialCellForRowAtIndexPath 而不是通常的调用......这不是一个干净的,直接的,面向对象的解决方案。
注意:我很感激您可以通过某种方式将 NSObject 子类化以覆盖信号(例如 here 所讨论的),但这不是语言解决方案。
github link -> 可能包含更多更新代码
回答问题:不可能以子class的形式覆盖UITableViewController和UITableViewDataSource之间的标准函数流。
UIKit源代码就像一个大黑盒子,我们看不到也改不了。 (如果您这样做,应用程序将被拒绝。)要完全按照您的意愿进行操作,您需要重写调用 UITableViewDataSource 函数的函数,以便它们指向第三个函数而不是协议函数。第三个函数将改变基本行为并从 UITableViewDataSource 触发函数。这样对于其他开发者来说一切都会保持不变。
Hack : Subclass 整个 UITableviewController -> 你需要存储的属性。这样其他人就可以 subclass 您的自定义 class 而他们将看不到任何 magic/mess 的幕后花絮。
下面的class使用了与常规UITableViewController相同的样式。用户覆盖他们希望改变的方法。因为这些方法是在现有函数中使用的,所以您获得了改变的功能。
遗憾的是,无法将这些函数标记为私有。
indexPath 的适配器存储 Bool
和原始的 indexPath。 -> 这将对应于您的数据。
新插入的单元格将根据创建它们的部分和一个计数器获得一个索引路径。 -> 可能会有用。
更新:在 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)
}
}
}
想象一个 table 视图控制器 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
会 "take over" 而实际上 return 51.
对于 cellForRowAtIndexPath,它会 "take over" 和 return 它自己的第四行单元格,它会 return 你的单元格行 N 从 0 到 3,它会 return 四行以上的单元格行减一。
如何在 ExtraRowTableViewController
中实现?
所以SomeList的程序员根本不需要做任何改变。
您会子类化 UITableView 或数据源委托 .. 还是??
为了澄清,一个示例用例可能是,比方说,在第四行添加广告、编辑字段或一些特殊新闻。 SomeList 的程序员不需要做任何事情来实现这一点是合适的,即它是以完全面向对象的方式实现的。
请注意,当然,添加新的 "substitute" 调用很容易,您的 table 视图将 "just know" 使用这些调用来代替普通调用。 (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] )
}
在示例中,您的 table 视图程序员必须 "just know" 他们必须在 SpecialTableViewController 中使用 specialNumberOfRowsInSection 和 specialCellForRowAtIndexPath 而不是通常的调用......这不是一个干净的,直接的,面向对象的解决方案。
注意:我很感激您可以通过某种方式将 NSObject 子类化以覆盖信号(例如 here 所讨论的),但这不是语言解决方案。
github link -> 可能包含更多更新代码
回答问题:不可能以子class的形式覆盖UITableViewController和UITableViewDataSource之间的标准函数流。
UIKit源代码就像一个大黑盒子,我们看不到也改不了。 (如果您这样做,应用程序将被拒绝。)要完全按照您的意愿进行操作,您需要重写调用 UITableViewDataSource 函数的函数,以便它们指向第三个函数而不是协议函数。第三个函数将改变基本行为并从 UITableViewDataSource 触发函数。这样对于其他开发者来说一切都会保持不变。
Hack : Subclass 整个 UITableviewController -> 你需要存储的属性。这样其他人就可以 subclass 您的自定义 class 而他们将看不到任何 magic/mess 的幕后花絮。
下面的class使用了与常规UITableViewController相同的样式。用户覆盖他们希望改变的方法。因为这些方法是在现有函数中使用的,所以您获得了改变的功能。
遗憾的是,无法将这些函数标记为私有。
indexPath 的适配器存储 Bool
和原始的 indexPath。 -> 这将对应于您的数据。
新插入的单元格将根据创建它们的部分和一个计数器获得一个索引路径。 -> 可能会有用。
更新:在 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)
}
}
}