从UITableViewCell引用到父UITableView?

时间:2009-07-10 16:05:26

标签: iphone objective-c cocoa-touch

有没有办法从UITableView内访问拥有UITableViewCell

7 个答案:

答案 0 :(得分:116)

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

这比依赖self.superview更好地始终完全是tableView是脆弱的。谁知道Apple将来如何重新组织UITableView的视图层次结构。

答案 1 :(得分:18)

这是一种更好的方法,它不依赖于任何特定的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。你的观点有多深?另一方面,保持对每个单元格的引用具有最小的内存惩罚(弱引用毕竟是一个指针),并且通常在不需要它们的地方添加对象关系被认为是一个糟糕的OO设计实践,原因很多,并且应该(请参阅下面的评论中的详细信息)。

更重要的是,将表引用保留在单元格内会增加代码复杂性并导致错误,因为UITableViewCells是可重用的。 UIKit不包含cell.parentTable属性并非巧合。如果您定义自己的代码,则必须添加代码来管理它,如果您没有有效地执行此操作,则可能会引入内存泄漏(即,单元格在其表的生命周期内存活)。

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

答案 2 :(得分:13)

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)

    }
}

实现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中更改其功能。

有人可能想告诉我它是否可以保存使用,只是为了确定。

答案 3 :(得分:7)

在构造表格视图单元格时,必须将引用添加回UITableView。

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

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

答案 4 :(得分:4)

如果您有UITableViewCells的自定义类,则可以在单元格的标题中添加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;
}

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

啊,带我回到闪存编程的美好时光。

答案 5 :(得分:1)

其他答案中的两种方法是:(A)存储对表的引用,或(B)向上查看超级视图。

我总是使用像(A)这样的模型对象和(B)用于表格单元格。

细胞

如果您正在处理UITableViewCell,那么AFAIK您必须手头有UITableView(比如你在表委托方法中),或者正在处理视图层次结构中的可见单元格。否则,你可能会做错事(请注意“很可能”)。

单元格被大量重复使用,如果碰巧有一个不可见的单元格,那么单元格存在的唯一真正原因是因为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更新不那么脆弱(例如有一天它们可能会使每个reuseidentifier存在UITableViewCell重用缓存,而不是每个tableview的每个reuseidentifier,那天使用弱引用方法的所有实现都会破坏)。

模型方法将用于更改单元格中显示的数据(即模型更改),因为更改将在模型显示的任何位置传播(例如,应用程序中其他位置的其他一些UIViewController) ,伐木,......)

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

无论哪种方式,使用单元测试而不是假设看起来更干净的代码在更新iOS时才起作用。

答案 6 :(得分:1)

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