有没有办法从UITableViewCell
中访问所属的UITableView
?
有没有办法从UITableViewCell
中访问所属的UITableView
?
在单元格中存储对tableView的weak
引用,这需要在表格的数据源方法-tableView:cellForRowAtIndexPath:
中设置。
这比依赖于self.superview
始终准确地是tableView要更加健壮。谁知道苹果将来会如何重新组织UITableView
的视图层次结构。
以下是更好的方法,不依赖于特定的UITableView层次结构。只要UITableView
类名不完全更改,它将适用于任何未来的iOS版本。虽然这种情况极不可能发生,但如果确实发生了,您无论如何都必须修改代码。
只需导入下面的类别,使用[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。您的视图有多深?另一方面,每个单元格保留一个引用只会有很小的内存开销(弱引用毕竟是指针),通常在不需要它们的地方添加对象关系被认为是一种不良的OO设计实践,应该避免(请参见下面的评论中的详细信息)。
更重要的是,将表格引用放在单元格内部会增加代码复杂性并可能导致错误,因为UITableViewCells
是可重用的。UIKit
中没有包含cell.parentTable
属性,这不是巧合。如果您定义了自己的属性,必须添加代码来管理它,如果未能有效地执行此操作,可能会引入内存泄漏(即,单元格的生命周期超过其表格的生命周期)。
由于通常您将在用户与单元格交互时使用上述类别(仅对单个单元格执行),而不是在[tableView:cellForRowAtIndexPath:]
中布置表格(对所有可见单元格执行),因此运行时成本应该是微不足道的。
UITableViewCell
受 UIKit
的设计约束,必须位于 UITableView
下方。以 UIView hierarchy-agnostic
的方式查找 UITableView
实例不会引入额外的关系。在软件工程中,这时称 UITableViewCell
为松散耦合(loosely coupled
)。相比之下,将特定的 UITableView
实例的引用保持在 UITableViewCell
内被认为是紧密耦合的,因为会引入直接关系。在UIKit中,没有提供 UITableViewCell.parentTable
属性是有原因的。 - DTsXcode 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)
}
}
实现DataSource和Delegate的表格视图类内部
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
的函数。
不过,有人可能想告诉我是否安全使用,以确保安全。
cell.updateCallback2 = UpdateCallback2
? - CularBytesupdateCallback?(updateList : true)
。如果回调被设置了,调用就会发生。 - vguerra在构建表视图单元格时,您必须添加对UITableView的引用。
然而,您真正想要的几乎肯定是对UITableViewController的引用...这需要相同的操作,在构建单元格时将其设置为单元格的代理并将其交给表视图。
如果您正在连接操作,则可以使用另一种方法,在IB中构建单元格,并将表视图控制器设置为文件所有者-然后将单元格中的按钮连接到表视图控制器中的操作。当您使用loadNibNamed加载单元格xib时,将视图控制器作为所有者传递,按钮操作将被连接回表视图控制器。
如果您的UITableViewCells有自定义类,您可以在cell的头文件中添加一个id类型的变量,并将变量合成。当您加载单元格时设置变量后,您可以自由地对tableview或任何其他更高级别的视图进行操作,而不需要太多麻烦或开销。
cell.h
// interface
id root;
// propery
@property (nonatomic, retain) id root;
cell.m
@synthesize root;
tableviewcontroller.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// blah blah, traditional cell declaration
// but before return cell;
cell.root = tableView;
}
@property (nonatomic, assign) id root;
- João Josézinho+ (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次迭代对应用程序性能的影响,请不要担心。
如果您谈论的是在单元格中显示的模型对象,那么该模型肯定可以/应该知道其父模型,这可能用于查找或触发更改表格(s)中的单元格模型可能会显示。 这就像(A),但随着未来iOS更新的变化而变得不太脆弱(例如,有一天他们可能使UITableViewCell重用缓存存在于重用标识符上,而不是每个tableview的重用标识符,到那一天,所有使用弱引用方法的实现都将失败)。
model方法将用于更改单元格中显示的数据(即模型更改),因为更改将在模型显示的任何地方传播(例如,应用程序中的其他UIViewController,记录日志等)
cell方法将用于tableview操作,如果单元格甚至不是表的子视图,则很可能总是一个坏主意(虽然这是您的代码,请随意)。
无论哪种方式,都请使用单元测试,而不是假设看似更干净的代码在更新iOS时就可以正常工作。
UITableView *tv = (UITableView *) self.superview.superview;
UITableViewController *vc = (UITableViewController *) tv.dataSource;
cell.superview
可能不是表格,因此它在这里对UITableView的实现做出了假设。 - nonopolarity