objective-c 从 UITableViewCell 到父 UITableView 的引用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1110482/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:53:56  来源:igfitidea点击:

Reference from UITableViewCell to parent UITableView?

iphoneobjective-ccocoa-touch

提问by Michael Grinich

Is there any way to access the owning UITableViewfrom within a UITableViewCell?

有没有办法UITableViewUITableViewCell.

回答by jbrennan

Store a weakreference to the tableView in the cell, which you'd set in -tableView:cellForRowAtIndexPath:of your table's dataSource.

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

This is better than relying on self.superviewto always be exactly the tableView is fragile. Who knows how Apple might re-organize the view hierarchy of UITableViewin the future.

这总比依赖于self.superviewtableView 是脆弱的要好。谁知道苹果UITableView将来会如何重新组织视图层次结构。

回答by DTs

Here's a nicer way to do it, which does not rely on any particular UITableView hierarchy. It will work with any future iOS version, provided that UITableViewdoes not change classname altogether. Not only this is extremely unlikely, but if it does happen you will have to retouch your code anyway.

这是一个更好的方法,它不依赖于任何特定的 UITableView 层次结构。它将适用于任何未来的 iOS 版本,前提是UITableView不会完全更改类名。这不仅是极不可能的,而且如果确实发生了,您无论如何都必须修改您的代码。

Just import the category below and get your reference with [myCell parentTableView]

只需导入下面的类别并获取您的参考 [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.


UPDATE
There is some discussion in the comments about whether keeping a weak reference is a better approach. It depends on your circumstances. Traversing the view hierarchy has some small runtime penalty as you are looping until the target UIView is identified. How deep are your views? On the other hand, keeping a reference on every cell has a minimal memory penalty (a weak reference is a pointer after all), and generally adding object relationships where they are not needed is considered a bad OO design practice for many reasons, and should be avoided (see details in the comments below).


更新
评论中有一些关于保持弱引用是否是更好的方法的讨论。这取决于你的情况。遍历视图层次结构会有一些小的运行时损失,因为您正在循环直到识别出目标 UIView。你的观点有多深?另一方面,在每个单元格上保留一个引用具有最小的内存损失(弱引用毕竟是一个指针),并且通常在不需要它们的地方添加对象关系出于多种原因被认为是糟糕的 OO 设计实践,并且应该避免(请参阅下面评论中的详细信息)。

More importantly, keeping table references inside cells adds code complexity and can lead to errors, because UITableViewCellsare reusable. It is no coincidence that UIKitdoes not include a cell.parentTableproperty. If you define your own you must add code to manage it, and if you fail to do so effectively you can introduce memory leaks (i.e., cells live past the lifetime of their table).

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

Because typically you'll be using the category above when a user interacts with a cell (execute for a single cell), and not when laying-out the table in [tableView:cellForRowAtIndexPath:](execute for all visible cells), the runtime cost should be insignificant.

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

回答by CularBytes

Xcode 7 beta, Swift 2.0

Xcode 7 测试版,Swift 2.0

This works fine for me, in my opinion it has nothing to do with the hierarchy or whatever. I had no trouble with this approach so far. I've used this for many async callbacks (ex. when an API request is done).

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

TableViewCell class

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)

    }
}

Inside table view class that implements the DataSource and Delegate

实现 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()
}

Ofcourse you can put any variable in the updateCallbackand change it's function in the tableViewaccordingly.

当然,您可以将任何变量放入updateCallback并相应地更改其功能tableView

Someone might want to tell me if it is save to use though, just to be sure.

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

回答by Kendall Helmstetter Gelner

You have to add a reference back to the UITableView when you construct the table view cell.

在构建表格视图单元格时,您必须添加一个对 UITableView 的引用。

However, almost certainly what you really want is a reference to your UITableViewController... that requires the same thing, set it as a delegate of the cell when you build the cell and hand it to the table view.

但是,几乎可以肯定,您真正想要的是对 UITableViewController 的引用……这需要同样的事情,在构建单元格并将其传递给表格视图时将其设置为单元格的委托。

An alternate approach if you are wiring up actions is to build the cells in IB, with the table view controller as the files owner - then wire up buttons in the cell to actions in the table view controller. When you load the cell xib with loadNibNamed, pass in the view controller as the owner and the button actions will be wired back to the table view controller.

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

回答by Kendall Helmstetter Gelner

If you have custom classes for your UITableViewCells, you can add an id type variable in your cell's header, and synthesize the variable. After you set the variable when you load the cell, you are free to do what you please with the tableview or any other higher view without much hassle or overhead.

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

cell.h

细胞.h

 // interface
 id root;

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

cell.m

细胞

@synthesize root;

@合成根;

tableviewcontroller.m

tableviewcontroller.m

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

Now you can call any of the tableview's methods from within your cell using the root variable. (e.g., [root reloadData]);

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

Ah, takes me back to the good old days of flash programming.

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

回答by wils

The two methods in other answers are: (A) store a reference to the table, or (B) walk up the superviews.

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

I'd always use something like (A) for model objects and (B) for table cells.

我总是对模型对象使用(A)之类的东西,对表格单元使用(B)之类的东西。

Cells

细胞

If you are dealing with a UITableViewCell, then AFAIK you must either have the UITableView at hand (say you are in a table delegate method), or are dealing with a visible cell that is in the view hierarchy. Otherwise, you may well be doing something wrong (please note the "may well").

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

Cells are liberally reused and if you happen to have one that is not visible then the only real reason that cell exists is because of iOS UITableView performance optimization (a slower iOS version would have released and hopefully dealloc'd the cell when it moved off screen) or because you have a specific reference to it. I guess this is probably the reason that table cells are not endowed with a tableView instance method.

单元格被大量重用,如果您碰巧有一个不可见的单元格,那么单元格存在的唯一真正原因是因为 iOS UITableView 性能优化(较慢的 iOS 版本会发布并希望在单元格移出屏幕时解除分配) ) 或者因为你有一个特定的参考。我想这可能是表格单元格没有被赋予 tableView 实例方法的原因。

So (B) gives the right result for all iOS's so far, and all future ones until they radically change how views work.

所以 (B) 为目前所有的 iOS 以及所有未来的 iOS 提供了正确的结果,直到它们从根本上改变视图的工作方式。

Though in order to avoid writing generalizable code over and over, I'd use this:

虽然为了避免一遍又一遍地编写可泛化的代码,我会使用这个:

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

and a convenience method:

和一个方便的方法:

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

(or categories if you like)

(或您喜欢的类别)

BTW, if you are concerned about the effect of a loop with 20 or so iterations of that size on your app performance,.. don't.

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

Models

楷模

If you are talking about the model object that is displayed in the cell, then definitely that model could/should know about its parent model, which may be used to find, or trigger changes in, the table(s) that the cell's model might be displayed in. This is like (A), but less brittle with future iOS updates (eg one day they might make the UITableViewCell reuse cache exist per reuseidentifier, rather than per reuseidentifier per tableview, on that day all the implementations that use the weak reference method will break).

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

Th modelmethod would be used for changes to the data displayed in the cell (i.e. model changes) since changes will propagate wherever the model is displayed (eg. some other UIViewController somewhere else in the app, logging, ...)

模型将用于更改数据的方法在小区(即模型的改变)显示,因为无论是显示模式的改变将传播(如一些其他的UIViewController别的地方的应用程序,记录,...)

The cellmethod would be used for tableview actions, which would likely always be a bad idea if the cell isn't even a subview of a table (though it's your code, go nuts).

细胞的方法将被用于实现代码如下行动,这将可能永远是一个坏主意,如果该小区不是表甚至一个子视图(虽然这是你的代码,发疯)。

Either way, use a unit test rather than assuming that seemingly cleaner code just works when they update iOS.

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

回答by Gank

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