如何知道UITableview的行号

发布于 2025-01-05 11:49:55 字数 121 浏览 1 评论 0原文

我有一个 UITableViewCell ,其中 UISwitch 作为每个单元格的附件视图。当我更改单元格中开关的值时,如何知道开关位于哪一行?我需要开关值更改事件中的行号。

I have a UITableViewCell with UISwitch as accessoryview of each cell. When I change the value of the switch in a cell, how can I know in which row the switch is? I need the row number in the switch value changed event.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

抽个烟儿 2025-01-12 11:49:55

标签、子类或视图层次导航工作量太大!。在你的操作方法中执行此操作:

CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView]; 
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];

适用于任何类型的视图、多节表,无论你能扔什么——只要你的发件人的来源在单元格的框架内(感谢 rob!),通常情况就是这样。

这是 UITableView Swift 扩展中的内容:

extension UITableView {
    func indexPath(for view: UIView) -> IndexPath? {
        let location = view.convert(CGPoint.zero, to: self)
        return self.indexPathForRow(at: location)
    }
}

Tags, subclasses, or view hierarchy navigation are too much work!. Do this in your action method:

CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView]; 
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];

Works with any type of view, multi section tables, whatever you can throw at it - as long as the origin of your sender is within the cell's frame (thanks rob!), which will usually be the case.

And here it is in a UITableView Swift extension:

extension UITableView {
    func indexPath(for view: UIView) -> IndexPath? {
        let location = view.convert(CGPoint.zero, to: self)
        return self.indexPathForRow(at: location)
    }
}
江湖彼岸 2025-01-12 11:49:55

如果将 tag 属性设置为行号(如其他答案所建议的),则每次都必须在 tableView:cellForRowAtIndexPath: 中更新它(因为单元格可以是重复用于不同的行)。

相反,当您需要行号时,您可以从 UISwitch (或任何其他视图)沿着 superview 链向上走到 UITableViewCell,然后到 UITableView,并向表视图询问单元格的索引路径:

static NSIndexPath *indexPathForView(UIView *view) {
    while (view && ![view isKindOfClass:[UITableViewCell class]])
        view = view.superview;
    if (!view)
        return nil;
    UITableViewCell *cell = (UITableViewCell *)view;
    while (view && ![view isKindOfClass:[UITableView class]])
        view = view.superview;
    if (!view)
        return nil;
    UITableView *tableView = (UITableView *)view;
    return [tableView indexPathForCell:cell];
}

这不需要 tableView:cellForRowAtIndexPath: 中的任何内容。

If you set the tag property to the row number (as suggested by other answers), you have to update it every time in tableView:cellForRowAtIndexPath: (because a cell can be reused for different rows).

Instead, when you need the row number, you can walk up the superview chain from the UISwitch (or any other view) to the UITableViewCell, and then to the UITableView, and ask the table view for the index path of the cell:

static NSIndexPath *indexPathForView(UIView *view) {
    while (view && ![view isKindOfClass:[UITableViewCell class]])
        view = view.superview;
    if (!view)
        return nil;
    UITableViewCell *cell = (UITableViewCell *)view;
    while (view && ![view isKindOfClass:[UITableView class]])
        view = view.superview;
    if (!view)
        return nil;
    UITableView *tableView = (UITableView *)view;
    return [tableView indexPathForCell:cell];
}

This doesn't require anything in tableView:cellForRowAtIndexPath:.

东京女 2025-01-12 11:49:55

cellForRowAtIndexPath: 中,将控件的 tag 属性设置为 indexPath.row

in cellForRowAtIndexPath:, set the tag property of your control to indexPath.row

马蹄踏│碎落叶 2025-01-12 11:49:55

公认的解决方案是一个聪明的黑客。

但是,如果我们可以利用 UIView 上已有的 tag 属性,为什么还需要使用 hitpoint 呢?您可能会说标签只能存储行或部分,因为它是单个Int

好吧...别忘了你们的根,伙计们(CS101)。
一个Int可以存储两个小两倍的整数。
这是一个扩展:

extension Int {

    public init(indexPath: IndexPath) {
        var marshalledInt: UInt32 = 0xffffffff

        let rowPiece = UInt16(indexPath.row)
        let sectionPiece = UInt16(indexPath.section)
        marshalledInt = marshalledInt & (UInt32(rowPiece) << 16)
        marshalledInt = marshalledInt + UInt32(sectionPiece)

        self.init(bitPattern: UInt(marshalledInt))
    }

    var indexPathRepresentation: IndexPath {
        let section = self & 0x0000ffff

        let pattern: UInt32 = 0xffff0000
        let row = (UInt32(self) & pattern) >> 16
        return IndexPath(row: Int(row), section: Int(section))
    }
}

在您的 tableView(_:, cellForRowAt:) 中,您可以:

cell.yourSwitch.tag = Int(indexPath: indexPath)

然后在操作处理程序中您可以:

func didToogle(sender: UISwitch){
    print(sender.tag.indexPathRepresentation)
}

但是请注意它的限制:row 和部分需要不大于 65535。 (UInt16.max)

我怀疑你的 tableView 的索引会那么高,但如果是的话,挑战自己并实施更有效的打包方案。假设如果我们有一个非常小的节,则不需要所有 16 位来表示一个节。我们可以有这样的 int 布局:

{section area length}{all remaining}[4 BITS: section area length - 1]

即我们的 4 个 LSB 表示节区域的长度 - 1,假设我们为节分配至少 1 位。因此,如果我们的部分为 0,则该行最多可以占用 27 位 ([1][27][4]),这绝对应该足够了。

Accepted solution is a clever hack.

However why do we need to use hitpoint if we can utilize already available tag property on UIView? You would say that tag can store only either row or section since its a single Int.

Well... Don't forget your roots guys (CS101).
A single Int can store two twice-smaller size integers.
And here is an extension for this:

extension Int {

    public init(indexPath: IndexPath) {
        var marshalledInt: UInt32 = 0xffffffff

        let rowPiece = UInt16(indexPath.row)
        let sectionPiece = UInt16(indexPath.section)
        marshalledInt = marshalledInt & (UInt32(rowPiece) << 16)
        marshalledInt = marshalledInt + UInt32(sectionPiece)

        self.init(bitPattern: UInt(marshalledInt))
    }

    var indexPathRepresentation: IndexPath {
        let section = self & 0x0000ffff

        let pattern: UInt32 = 0xffff0000
        let row = (UInt32(self) & pattern) >> 16
        return IndexPath(row: Int(row), section: Int(section))
    }
}

In your tableView(_:, cellForRowAt:) you can then:

cell.yourSwitch.tag = Int(indexPath: indexPath)

And then in the action handler you would can:

func didToogle(sender: UISwitch){
    print(sender.tag.indexPathRepresentation)
}

However please note it's limitation: row and section need to be not larger then 65535. (UInt16.max)

I doubt your tableView's indexes will go that high but in case they do, challenge yourself and implement more efficient packing scheme. Say if we have a section very small, we don't need all 16 bits to represent a section. We can have our int layout like:

{section area length}{all remaining}[4 BITS: section area length - 1]

that is our 4 LSBs indicate the length of section area - 1, given that we allocate at least 1 bit for a section. Thus in case of our section is 0, the row can occupy up to 27 bits ([1][27][4]), which definitely should be enough.

海未深 2025-01-12 11:49:55

我更喜欢使用子视图,如果你知道你的布局,它通常非常简单并且只有 1 行短...

    NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];

就是这样,如果它更多嵌套,添加更多超级视图。

更多信息:

您所做的只是询问父视图及其父视图(即单元格)。然后,您向表格视图询问刚刚获得的单元格的索引路径。

I prefer using subviews, if you know your layout it's generally super simple and 1 line short...

    NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];

Thats it, if its more nested, add in more superviews.

Bit more info:

all you are doing is asking for the parent view and its parent view which is the cell. Then you are asking your tableview for the indexpath of that cell you just got.

北方。的韩爷 2025-01-12 11:49:55

一种常见的方法是将控件(在您的情况下为开关)的 tag 设置为可用于标识行或表示的对象的内容。

例如,在 tableView:cellForRowAtIndexPath: 中,将开关的 tag 属性设置为 indexPath.row ,在您的操作方法中,您可以获得来自发件人的标签。

就我个人而言,我不喜欢这种方法,更喜欢子类化 UITableViewCell。
此外,向标签添加“偏移量”可能是一个好主意,以防止与其他视图的标签发生任何冲突。

One common way to do this is to set the tag of the control (in your case the switch) to something that can be used to identify the row or represented object.

For example, in tableView:cellForRowAtIndexPath: set the tag property of the switch to the indexPath.row and in your action method you can get the tag from the sender.

Personally, I don't like this approach and prefer subclassing UITableViewCell.
Also, it may be a good idea to add an "offset" to the tag to prevent any conflicts with the tags of other views.

小耗子 2025-01-12 11:49:55

这篇文章中接受的答案非常好。我想向读者建议,以下源自这篇文章中的 @robmayoff 的内容也完全没问题:

- (NSIndexPath *)indexPathForView:(UIView *)view inTableView:(UITableView *)tableView {
    while (view && ![view isKindOfClass:[UITableViewCell class]])
        view = view.superview;
    UITableViewCell *cell = (UITableViewCell *)view;
    return [tableView indexPathForCell:cell];
}

有些人断言,由于 while 循环,这种方法包含太多的计算工作。另一种方法是将视图的原点转换为表视图坐标空间并调用indexPathForRowAtPoint:,隐藏更多工作。

有些人断言,相对于潜在的 SDK 更改,这种方法是不安全的。事实上,Apple 已经更改了一次 tableview 单元格层次结构,向单元格添加了一个 contentView 。这种方法在此类更改之前和之后都有效。只要可以通过超级视图链找到视图祖先(这与 UIKit 中的任何内容一样基本),这就是好代码。

The accepted answer on this post is perfectly fine. I'd like to suggest to readers that the following, derived from @robmayoff on this post, is also perfectly fine:

- (NSIndexPath *)indexPathForView:(UIView *)view inTableView:(UITableView *)tableView {
    while (view && ![view isKindOfClass:[UITableViewCell class]])
        view = view.superview;
    UITableViewCell *cell = (UITableViewCell *)view;
    return [tableView indexPathForCell:cell];
}

Some have asserted that this approach contains too much computational work because of the while loop. The alternative, convert the view's origin to table view coordinate space and call indexPathForRowAtPoint:, hides even more work.

Some have asserted that this approach is unsafe relative to potential SDK changes. In fact, Apple has already changed the tableview cell hierarchy once, adding a contentView to the cell. This approach works before and after such a change. As long as view ancestors can be found via a chain of superviews (which is as fundamental as anything in UIKit), this is good code.

心房的律动 2025-01-12 11:49:55

一位同事建议了以下内容,我将其放入 UITableView 类别中:

+(UITableViewCell*)findParentCellForSubview:(UIView*)view
{
    while (([view isKindOfClass:[UITableViewCell class]] == NO) && ([view superview] != nil))
        view = [view superview];

    if ([view superview] != nil)
        return (UITableViewCell*)view;

    return nil;
}

仍然很简单 - 但它有效。

A colleague suggested the following, which I made into a UITableView category:

+(UITableViewCell*)findParentCellForSubview:(UIView*)view
{
    while (([view isKindOfClass:[UITableViewCell class]] == NO) && ([view superview] != nil))
        view = [view superview];

    if ([view superview] != nil)
        return (UITableViewCell*)view;

    return nil;
}

Still hackly - but it works.

烂人 2025-01-12 11:49:55

使用 superView 的另一种变体。与 UIView 的类别类似。

- (UITableViewCell *)superCell
{
    if (!self.superview) {
        return nil;
    }

    if ([self.superview isKindOfClass:[UITableViewCell class]]) {
        return (UITableViewCell *)self.superview;
    }

    return [self.superview superCell];
}

One more variant of using superView. Works like category for UIView.

- (UITableViewCell *)superCell
{
    if (!self.superview) {
        return nil;
    }

    if ([self.superview isKindOfClass:[UITableViewCell class]]) {
        return (UITableViewCell *)self.superview;
    }

    return [self.superview superCell];
}
时光暖心i 2025-01-12 11:49:55

我不知道多个部分,但我可以为您提供一个部分...

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index=indexPath.row;
NSString *string=[[NSString alloc]initWithFormat:@"%ld",(long)index];
}

从中您可以获得行号,然后可以将其保存到字符串中...

i dont know about the multiple sections but i can give you for the one section...

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index=indexPath.row;
NSString *string=[[NSString alloc]initWithFormat:@"%ld",(long)index];
}

from this you can get the row number and you can save it to the string....

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文