从 UITableViewCell 引用到父 UITableView? ?

有没有办法从 UITableViewCell中访问所属的 UITableView

51921 次浏览

在单元格中存储对 tableView 的 weak引用,您将在表的 dataSource 的 -tableView:cellForRowAtIndexPath:中设置该引用。

这比依赖 self.superview总是准确地表示 tableView 是脆弱的要好。谁知道苹果将来会如何重新组织 UITableView的视图层次结构。

在构造表视图单元格时,必须向 UITableView 添加一个引用。

但是,几乎可以肯定的是,您真正需要的是对 UITableViewController 的引用... ... 它需要同样的东西,在构建单元格时将其设置为单元格的委托,并将其交给表视图。

如果要连接操作,另一种方法是在 IB 中构建单元格,将表视图控制器作为文件所有者,然后将单元格中的按钮连接到表视图控制器中的操作。当您使用 loadNibNamed 加载单元 xib 时,将视图控制器作为所有者传入,按钮操作将被连接回表视图控制器。

如果为 UITableViewCell 定制了自定义类,则可以在单元格的标头中添加 id 类型变量,并合成该变量。在加载单元格时设置了变量之后,就可以自由地对 tableview 或任何其他更高的视图执行任何您喜欢的操作,而不会带来太多麻烦或开销。

手机

 // interface
id root;


// propery
@property (nonatomic, retain) id root;

细胞

@ 合成根;

Tableviewcontroller

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// blah blah, traditional cell declaration
// but before return cell;
cell.root = tableView;
}

现在,您可以使用根变量在单元格内调用 tableview 的任何方法(例如,[ root reloadData ]) ;

让我想起了闪存编程的美好时光。

这里有一种更好的方法,它不依赖于任何特定的 UITableView 层次结构。它将与任何未来的 iOS 版本,只要 UITableView不改变类名完全。不仅这是极不可能的,而且如果发生这种情况,您将不得不修改您的代码。

只要导入下面的类别,并得到您的参考与 [myCell parentTableView]

@implementation UIView (FindUITableView)


-(UITableView *) parentTableView {
// iterate up the view hierarchy to find the table containing this cell/view
UIView *aView = self.superview;
while(aView != nil) {
if([aView isKindOfClass:[UITableView class]]) {
return (UITableView *)aView;
}
aView = aView.superview;
}
return nil; // this view is not within a tableView
}


@end


// To use it, just import the category and invoke it like so:
UITableView *myTable = [myTableCell parentTableView];


// It can also be used from any subview within a cell, from example
// if you have a UILabel within your cell, you can also do:
UITableView *myTable = [myCellLabel parentTableView];


// NOTE:
// If you invoke this on a cell that is not part of a UITableView yet
// (i.e., on a cell that you just created with [[MyCell alloc] init]),
// then you will obviously get nil in return. You need to invoke this on cells/subviews
// that are already part of a UITableView.


更新
在评论中有一些关于保留弱引用是否是一种更好的方法的讨论。这取决于你的情况。遍历视图层次结构会在循环过程中产生一些小的运行时损失,直到确定目标 UIView。你的观点有多深刻?另一方面,在每个单元格上保留一个引用只会带来最小的内存损失(弱引用毕竟是一个指针) ,通常在不需要的地方添加对象关系被认为是糟糕的面向对象设计实践,因此应该避免(详见下面的评论)。

更重要的是,将表引用保留在单元格中会增加代码复杂性,并可能导致错误,因为 UITableViewCells是可重用的。UIKit不包含 cell.parentTable属性并非巧合。如果您定义了自己的单元格,那么您必须添加代码来管理它,如果您不能有效地做到这一点,那么您可能会引入内存泄漏(例如,单元格的存活时间超过了它们的表的生命周期)。

因为通常当用户与单元格交互时(对单个单元格执行) ,而不是在 [tableView:cellForRowAtIndexPath:]中布局表(对所有可见单元格执行)时,您将使用上面的类别,所以运行时成本应该是微不足道的。

其他答案中的两个方法是: (A)存储对表的引用,或者(B)遍历超视图。

我总是对模型对象使用(A) ,对表单元格使用(B)。

细胞

如果您正在处理 UITableViewCell,那么 AFAIK 必须要么拥有 UITableView (假设您处于表委托方法中) ,要么正在处理视图层次结构中的可见单元格。否则,你很可能正在做一些错误的事情(请注意“ may well”)。

单元格可以自由地重用,如果你碰巧有一个不可见的单元格,那么单元格存在的唯一真正原因是因为 iOS UITableView 性能优化(一个较慢的 iOS 版本会发布,希望在单元格离开屏幕时释放它)或者因为你有一个特定的引用。 我猜这可能是表单元格没有被赋予 tableView 实例方法的原因。

所以(B)给出了迄今为止所有 iOS 和未来所有 iOS 的正确结果,直到它们从根本上改变了视图的工作方式。

不过,为了避免一遍又一遍地编写可泛化的代码,我会使用以下代码:

+ (id)enclosingViewOfView:(UIView *)view withClass:(Class)returnKindOfClass {
while (view&&![view isKindOfClass:returnKindOfClass]) view=view.superview;
return(view);
}

以及一种方便的方法:

+ (UITableView *)tableForCell:(UITableViewCell *)cell {
return([self enclosingViewOfView:cell.superview withClass:UITableView.class]);
}

(或类别,如果你喜欢)

顺便说一句,如果你担心一个循环有20个左右的大小的迭代对你的应用程序性能的影响,。.不要。

模特

如果你说的是单元格中显示的模型对象,那么这个模型肯定可以/应该知道它的父模型,这个父模型可以用来查找或触发单元格模型可能显示的表中的更改。 这类似于(A) ,但是对于未来的 iOS 更新来说不那么脆弱(例如,有一天他们可能会让 UITableViewCell 重用缓存存在于每个重用标识符中,而不是每个表视图的每个重用标识符中,在那一天,所有使用弱引用方法的实现都会中断)。

模特方法将用于对单元格中显示的数据进行更改(即模型更改) ,因为更改将在显示模型的任何地方传播(例如,应用程序中的其他某个 UIViewController、日志记录等)

手机方法将用于表视图操作,如果单元格甚至不是表的子视图(尽管这是您的代码,那么这可能总是一个坏主意)。

无论哪种方式,使用单元测试,而不是假设看似简洁的代码在更新 iOS 时能够正常工作。

UITableView *tv = (UITableView *) self.superview.superview;
UITableViewController *vc = (UITableViewController *) tv.dataSource;

Xcode 7 beta Swift 2.0

这对我来说很好,在我看来,这与等级制度或其他什么无关。到目前为止,我对这种方法没有任何问题。我已经在许多异步回调中使用了它(例如。当 API 请求完成时)。

TableViewCell 类

class ItemCell: UITableViewCell {


var updateCallback : ((updateList: Bool)-> Void)? //add this extra var


@IBAction func btnDelete_Click(sender: AnyObject) {
let localStorage = LocalStorage()
if let description = lblItemDescription.text
{
//I delete it here, but could be done at other class as well.
localStorage.DeleteItem(description)
}
updateCallback?(updateList : true)


}
}

实现数据源和委托的内部表视图类

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: ItemCell = self.ItemTableView.dequeueReusableCellWithIdentifier("ItemCell") as! ItemCell!
cell.updateCallback = UpdateCallback //add this extra line
cell.lblItemDescription?.text = self.SomeList[indexPath.row].Description
return cell
}


func UpdateCallback(updateTable : Bool) //add this extra method
{
licensePlatesList = localStorage.LoadNotificationPlates()
LicenseTableView.reloadData()
}

当然你可以把任何变量放在 updateCallback中,然后相应地改变它在 tableView中的函数。

有人可能想告诉我,如果它是保存使用,虽然,只是为了确保。