当一个元素被激活时,如何获取indexpath.row?

How to get the indexpath.row when an element is activated?

提问人:Vincent 提问时间:2/22/2015 最后编辑:Vincent 更新时间:2/15/2022 访问量:161513

问:

我有一个带有按钮的表视图,我想在点击其中一个按钮时使用 indexpath.row。 这是我目前拥有的,但它始终是 0

var point = Int()
func buttonPressed(sender: AnyObject) {
    let pointInTable: CGPoint =         sender.convertPoint(sender.bounds.origin, toView: self.tableView)
    let cellIndexPath = self.tableView.indexPathForRowAtPoint(pointInTable)
    println(cellIndexPath)
    point = cellIndexPath!.row
    println(point)
}
iOS Swift iPhone UITableView

评论

0赞 Vincent 2/23/2015
我应该使用 IndexPathForSelectedRow() 而不是 point 变量吗?或者我应该在哪里使用它?

答:

3赞 Antonio 2/23/2015 #1

由于事件处理程序的发送方是按钮本身,因此我将使用按钮的属性来存储索引,并在 中初始化。tagcellForRowAtIndexPath

但是,如果再多做一些工作,我会以完全不同的方式去做。如果您使用的是自定义单元格,这就是我解决问题的方式:

  • 将“indexPath”属性添加到自定义表单元格
  • cellForRowAtIndexPath
  • 将 Tap 处理程序从 View Controller 移动到单元实现
  • 使用委派模式通知视图控制器有关 Tap 事件的信息,并传递索引路径

评论

0赞 Dave G 8/18/2015
安东尼奥,我有一个自定义单元,很想按照你的方式做这件事。但是,它不起作用。我希望我的“滑动显示删除按钮”代码运行,这是 tableView commitEditingStyle 方法。我从 mainVC 类中删除了该代码并将其放在 customCell 类中,但现在该代码不再起作用。我错过了什么?
0赞 Edward 4/26/2017
我认为这是获取具有 x 部分的单元格的 indexPath 的最佳方法,但是我认为在 MVC 方法中不需要项目符号 3 和 4
176赞 Jacob King 4/22/2015 #2

Giorasc的回答几乎做到了,但他忽略了一个事实,即Cell有一个额外的层。因此,我们必须更深入一层:contentView

guard let cell = sender.superview?.superview as? YourCellClassHere else {
    return // or fatalError() or whatever
}

let indexPath = itemTable.indexPath(for: cell)

这是因为在视图层次结构中,tableView 具有单元格作为子视图,这些子视图随后具有自己的“内容视图”,这就是为什么您必须获取此内容视图的超级视图才能获取单元格本身的原因。因此,如果您的按钮包含在子视图中,而不是直接包含在单元格的内容视图中,则必须深入多少层才能访问它。

以上就是这样一种方法,但不一定是最好的方法。虽然它是功能性的,但它假定了 Apple 从未记录过的细节,例如它的视图层次结构。这将在将来进行更改,因此上述代码的行为可能会变得不可预测。UITableViewCell

由于上述原因,出于使用寿命和可靠性的原因,我建议采用另一种方法。此线程中列出了许多替代方案,我鼓励您向下阅读,但我个人最喜欢的如下:

在单元格类上保留闭包的属性,让按钮的操作方法调用它。

class MyCell: UITableViewCell {
    var button: UIButton!

    var buttonAction: ((Any) -> Void)?

    @objc func buttonPressed(sender: Any) {
        self.buttonAction?(sender)
    }
}

然后,当您在 中创建单元格时,您可以为闭包分配一个值。cellForRowAtIndexPath

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! MyCell
    cell.buttonAction = { sender in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed(closure: buttonAction, indexPath: indexPath) // <- Method on the view controller to handle button presses.
}

通过将处理程序代码移动到此处,您可以利用已经存在的参数。这是一种比上面列出的方法更安全的方法,因为它不依赖于未记录的特征。indexPath

评论

2赞 Jacob King 7/22/2016
发现得很好。我是一个称职的开发人员,我保证;) - 修改了我的答案。
15赞 rmaddy 2/4/2017
这不是从按钮获取单元格的正确方法。多年来,单元格的布局发生了变化,当这种情况发生时,这样的代码将无法工作。请勿使用此方法。
12赞 bpapa 2/4/2017
这是一个糟糕的解决方案。它假定 Apple 从未同意的有关 UITableViewCells 的详细信息。虽然 UITableViewCells 具有 contentView 属性,但不能保证 contentView 的超级视图始终是 Cell。
1赞 Jacob King 11/8/2017
@PintuRajput 你能向我描述一下你的视图层次结构吗?您可能会看到这种情况,因为您的按钮不是单元格内容视图的直接子视图。
2赞 Jacob King 8/8/2018
@ymutlu我完全同意,我确实在答案中说明了这一点。我还提出了一个更强大的解决方案。我之所以保留原版,是因为我觉得最好用一种方法向其他开发人员展示问题,而不是完全回避它,这不会教他们你所看到的任何东西。:)
1赞 Avijit Nagare 2/7/2016 #3

我使用convertPoint方法从tableview获取点,并将此点传递给indexPathForRowAtPoint方法以获取indexPath

 @IBAction func newsButtonAction(sender: UIButton) {
        let buttonPosition = sender.convertPoint(CGPointZero, toView: self.newsTableView)
        let indexPath = self.newsTableView.indexPathForRowAtPoint(buttonPosition)
        if indexPath != nil {
            if indexPath?.row == 1{
                self.performSegueWithIdentifier("alertViewController", sender: self);
            }   
        }
    }
9赞 funclosure 2/22/2016 #4

Swift2.1

我找到了一种方法来做到这一点,希望它会有所帮助。

let point = tableView.convertPoint(CGPoint.zero, fromView: sender)

    guard let indexPath = tableView.indexPathForRowAtPoint(point) else {
        fatalError("can't find point in tableView")
    }

评论

0赞 OOProg 7/26/2016
如果错误运行,这意味着什么?在tableView中找不到点的原因是什么?
0赞 bpapa 2/4/2017
这(或类似,使用 UIView 转换方法)应该是可接受的答案。不知道为什么它现在是#4,因为它不对表视图的私有层次结构做出假设,它不使用 tag 属性(几乎总是一个坏主意),并且不涉及很多额外的代码。
61赞 Iron John Bonney 3/25/2016 #5

UPDATE:获取包含按钮(部分和行)的单元格的 indexPath:

使用按钮位置

在方法中,可以获取按钮的位置,将其转换为 tableView 中的坐标,然后获取该坐标处行的 indexPath。buttonTapped

func buttonTapped(_ sender:AnyObject) {
    let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}

注意:有时,当使用该函数导致在某个点查找行时,即使那里有一个 tableView 单元格,您也会遇到边缘情况。要解决此问题,请尝试传递一个与原点略有偏移的实际坐标,例如:view.convert(CGPointZero, to:self.tableView)nil

let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)

上一个答案: 使用 Tag 属性(仅返回行)

与其爬入 superview 树来获取指向包含 UIButton 的单元格的指针,不如使用上面 Antonio 提到的 button.tag 属性,该技术在此答案中进行了描述,如下所示:

在设置标记属性中:cellForRowAtIndexPath:

button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

然后,在函数中,引用该标记来获取按钮所在的 indexPath 行:buttonClicked:

func buttonClicked(sender:UIButton) {
    let buttonRow = sender.tag
}

我更喜欢这种方法,因为我发现在超视图树中摆动可能是设计应用程序的一种冒险方式。此外,对于objective-C,我过去使用过这种技术,并且对结果感到满意。

评论

6赞 Jacob King 4/16/2016
这是一个很好的方法,我会投票让你的代表开始一点,但是唯一的缺陷是,如果还需要,它不能访问 indexPath.section。不过答案很好!
0赞 Iron John Bonney 4/27/2016
谢谢雅各布!我很欣赏代表的业力。如果要获取 除了 之外(不将标记属性重置为 ),只需将标记更改为 ,然后在函数中,您可以使用 和 来访问这两个函数。indexPath.sectionindexPath.rowindexPath.sectioncellForRowAtIndexPath:button.tag = indexPathbuttonClicked:sender.tag.rowsender.tag.section
1赞 Jacob King 4/28/2016
这是一个新功能,因为我确定我记得标签属性是 Int 类型而不是 AnyObject 类型,除非在 swift 2.3 中更改?
0赞 Iron John Bonney 4/28/2016
@JacobKing你是对的!我的错是,我在写那条评论时完全间隔开来,并认为该标签是 AnyObject 类型。Derp - 别介意我。不过,如果您可以将 indexPath 作为标签传递,那将很有用......
3赞 bpapa 2/4/2017
也不是一个好的方法。首先,它只适用于具有单个部分的表视图。
69赞 Paulw11 8/14/2016 #6

我解决此类问题的方法是在单元格和表视图之间使用委托协议。这允许您将按钮处理程序保留在单元子类中,从而使您能够将修饰操作处理程序分配给 Interface Builder 中的原型单元,同时仍将按钮处理程序逻辑保留在视图控制器中。

它还避免了导航视图层次结构或使用属性的潜在脆弱方法,当单元格索引更改(由于插入、删除或重新排序)时,这种方法会出现问题tag

CellSubclass.swift

protocol CellSubclassDelegate: class {
    func buttonTapped(cell: CellSubclass)
}

class CellSubclass: UITableViewCell {

@IBOutlet var someButton: UIButton!

weak var delegate: CellSubclassDelegate?

override func prepareForReuse() {
    super.prepareForReuse()
    self.delegate = nil
}

@IBAction func someButtonTapped(sender: UIButton) {
    self.delegate?.buttonTapped(self)
}

视图控制器.swift

class MyViewController: UIViewController, CellSubclassDelegate {

    @IBOutlet var tableview: UITableView!

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSubclass

        cell.delegate = self

        // Other cell setup

    } 

    //  MARK: CellSubclassDelegate

    func buttonTapped(cell: CellSubclass) {
        guard let indexPath = self.tableView.indexPathForCell(cell) else {
            // Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
            return
        }

        //  Do whatever you need to do with the indexPath

        print("Button tapped on row \(indexPath.row)")
    }
} 

评论

0赞 Paulw11 10/24/2016
buttonTapped是委托函数,位于视图控制器中。在我的示例中,是单元格中的操作方法someButtonTapped
0赞 Bhavin Bhadani 10/24/2016
@paulw11我得到的单元格没有成员按钮在这个方法中点击@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
1赞 bpapa 2/4/2017
这是一个相当不错的解决方案(远不如目前拥有更多选票的两个解决方案那么糟糕,使用标签查看超级视图),但感觉要添加的额外代码太多了。
4赞 Robotic Cat 2/4/2017
这是正确的解决方案,应该是公认的答案。它不会滥用 tag 属性,不假定单元格的构造(Apple 可以很容易地更改),并且在移动单元格或在现有单元格之间添加新单元格时仍将工作(无需额外编码)。
1赞 Adrian 5/19/2017
@Paulw11 我最初以为这是很多代码,但事实证明它比我以前使用的要有弹性得多。感谢您发布这个强大的解决方案。
2赞 Jacob King 9/26/2016 #7

在看到 Paulw11 关于使用委托回调的建议后,我想稍微详细说明一下/提出另一个类似的建议。如果你不想使用委托模式,你可以在 swift 中使用闭包,如下所示:

您的单元格类别:

class Cell: UITableViewCell {
    @IBOutlet var button: UIButton!

    var buttonAction: ((sender: AnyObject) -> Void)?

    @IBAction func buttonPressed(sender: AnyObject) {
        self.buttonAction?(sender)
    }
}

您的方法:cellForRowAtIndexPath

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
    cell.buttonAction = { (sender) in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed // <- Method on the view controller to handle button presses.
}
0赞 Sean 1/25/2017 #8

在 Swift 3 中。还使用了保护语句,避免了一长串大括号。

func buttonTapped(sender: UIButton) {
    guard let cellInAction = sender.superview as? UITableViewCell else { return }
    guard let indexPath = tableView?.indexPath(for: cellInAction) else { return }

    print(indexPath)
}

评论

0赞 rmaddy 2/4/2017
这是行不通的。按钮的 superview 不会是单元格。
0赞 Sean 2/11/2017
这确实有效。您唯一应该注意的是,每个人的视图堆栈都不同。它可以是 sender.superview、sender.superview.superview 或 sender.superview.superview.superview。但它真的很好用。
21赞 Duncan C 2/5/2017 #9

使用 UITableView 的扩展来提取包含任何视图的单元格:


@Paulw11 的答案是设置具有将消息发送到表视图的委托属性的自定义单元格类型,这是一个很好的方法,但它需要一定的工作量来设置。

我认为遍历表视图单元格的视图层次结构以查找单元格是一个坏主意。它很脆弱 - 如果以后出于布局目的将按钮包含在视图中,则该代码可能会中断。

使用视图标签也很脆弱。您必须记住在创建单元格时设置标记,如果在将视图标记用于其他目的的视图控制器中使用该方法,则可能会有重复的标记号,并且代码可能无法按预期工作。

我创建了一个 UITableView 扩展,它允许您获取表视图单元格中包含的任何视图的 indexPath。如果传入的视图实际上不在表视图单元格内,则返回一个 null。下面是完整的扩展源文件。只需将此文件放入项目中,然后使用包含的方法查找包含任何视图的 indexPath。OptionalindexPathForView(_:)

//
//  UITableView+indexPathForView.swift
//  TableViewExtension
//
//  Created by Duncan Champney on 12/23/16.
//  Copyright © 2016-2017 Duncan Champney.
//  May be used freely in for any purpose as long as this 
//  copyright notice is included.

import UIKit

public extension UITableView {
  
  /**
  This method returns the indexPath of the cell that contains the specified view
   
   - Parameter view: The view to find.
   
   - Returns: The indexPath of the cell containing the view, or nil if it can't be found
   
  */
  
    func indexPathForView(_ view: UIView) -> IndexPath? {
        let center = view.center
        let viewCenter = self.convert(center, from: view.superview)
        let indexPath = self.indexPathForRow(at: viewCenter)
        return indexPath
    }
}

若要使用它,只需在单元格中包含的按钮的 IBAction 中调用方法即可:

func buttonTapped(_ button: UIButton) {
  if let indexPath = self.tableView.indexPathForView(button) {
    print("Button tapped at indexPath \(indexPath)")
  }
  else {
    print("Button indexPath not found")
  }
}

(请注意,仅当它传递的视图对象包含在当前屏幕上的单元格中时,该函数才起作用。这是合理的,因为不在屏幕上的视图实际上并不属于特定的 indexPath;当它包含单元格被回收时,它可能会被分配给不同的 indexPath。indexPathForView(_:)

编辑:

您可以从 Github 下载使用上述扩展的工作演示项目:TableViewExtension.git

评论

0赞 Jeremy Andrews 10/29/2017
谢谢,我使用扩展来获取单元格中文本视图的 indexPath - 工作完美。
0赞 Teena nath Paul 2/17/2017 #10

有时按钮可能位于 UITableViewCell 的另一个视图内。在这种情况下,superview.superview 可能不会给出单元格对象,因此 indexPath 将为 nil。

在这种情况下,我们应该继续寻找超视图,直到我们得到单元格对象。

通过superview获取单元格对象的函数

func getCellForView(view:UIView) -> UITableViewCell?
{
    var superView = view.superview

    while superView != nil
    {
        if superView is UITableViewCell
        {
            return superView as? UITableViewCell
        }
        else
        {
            superView = superView?.superview
        }
    }

    return nil
}

现在我们可以在按钮点击上获取 indexPath,如下所示

@IBAction func tapButton(_ sender: UIButton)
{
    let cell = getCellForView(view: sender)
    let indexPath = myTabelView.indexPath(for: cell)
}
11赞 Sajid Zeb 2/24/2018 #11

溶液:

单元格中有一个按钮 (myButton) 或任何其他视图。像这样在 cellForRowAt 中分配标签

cell.myButton.tag = indexPath.row

现在,在tapFunction或任何其他方法中。像这样取出它并将其保存在局部变量中。

currentCellNumber = (sender.view?.tag)!

在此之后,您可以在任何位置使用此 currentCellNumber 来获取所选按钮的 indexPath.row。

享受!

评论

0赞 Duncan C 1/26/2020
这种方法是可行的,但视图标签很脆弱,正如我的回答中提到的。例如,简单的整数标记不适用于分段表视图。(IndexPath 为 2 个整数。我的方法将始终有效,并且无需在按钮(或其他可点击的视图)中安装标签。
0赞 mramosch 10/17/2020
您可以轻松地将部分和行/项存储在一个整数中。看我的答案...
1赞 Vineeth Krishnan 3/13/2018 #12

尝试使用 #selector 调用 cellforrowatindexpath IBaction.In

            cell.editButton.tag = indexPath.row
        cell.editButton.addTarget(self, action: #selector(editButtonPressed), for: .touchUpInside)

这样,您就可以访问 editButtonPressed 方法中的索引路径

func editButtonPressed(_ sender: UIButton) {

print(sender.tag)//this value will be same as indexpath.row

}

评论

0赞 Amalendu Kar 4/15/2019
最合适的答案
0赞 koen 2/21/2020
否,当用户添加或删除单元格时,标签将处于关闭状态。
0赞 mramosch 10/17/2020
@koen:如果在插入或删除后重新加载 tableView,则不会;-)
7赞 DEEPAK KUMAR 6/13/2018 #13

在 Swift 4 中,只需使用以下命令:

func buttonTapped(_ sender: UIButton) {
        let buttonPostion = sender.convert(sender.bounds.origin, to: tableView)

        if let indexPath = tableView.indexPathForRow(at: buttonPostion) {
            let rowIndex =  indexPath.row
        }
}

评论

0赞 10000RubyPools 1/1/2019
最干净的答案,应该是被选中的。唯一需要注意的是,这是一个出口变量,需要在此答案起作用之前先引用。tableView
0赞 Parthpatel1105 4/15/2019
工作像魅力一样!!
0赞 DvixExtract 12/26/2019 #14

就我而言,我有多个部分,并且部分和行索引都至关重要,因此在这种情况下,我只是在 UIButton 上创建了一个属性,我设置了单元格 indexPath,如下所示:

fileprivate struct AssociatedKeys {
    static var index = 0
}

extension UIButton {

    var indexPath: IndexPath? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.index) as? IndexPath
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.index, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

然后在cellForRowAt中设置属性,如下所示:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
    cell.button.indexPath = indexPath
}

然后在 handleTapAction 中,您可以像这样获取 indexPath:

@objc func handleTapAction(_ sender: UIButton) {
    self.selectedIndex = sender.indexPath

}
2赞 Rashid Latif 2/20/2020 #15

Swift 4 和 5

方法 1:使用协议委托

例如,您有一个 with 名称UITableViewCellMyCell

class MyCell: UITableViewCell {
    
    var delegate:MyCellDelegate!
    
    @IBAction private func myAction(_ sender: UIButton){
        delegate.didPressButton(cell: self)
    }
}

现在创建一个protocol

protocol MyCellDelegate {
    func didPressButton(cell: UITableViewCell)
}

下一步,创建一个扩展UITableView

extension UITableView {
    func returnIndexPath(cell: UITableViewCell) -> IndexPath?{
        guard let indexPath = self.indexPath(for: cell) else {
            return nil
        }
        return indexPath
    }
}

在实现协议时UIViewControllerMyCellDelegate

class ViewController: UIViewController, MyCellDelegate {
     
    func didPressButton(cell: UITableViewCell) {
        if let indexpath = self.myTableView.returnIndexPath(cell: cell) {
              print(indexpath)
        }
    }
}

方法 2:使用闭包

UIViewController

override func viewDidLoad() {
        super.viewDidLoad()
       //using the same `UITableView extension` get the IndexPath here
        didPressButton = { cell in
            if let indexpath = self.myTableView.returnIndexPath(cell: cell) {
                  print(indexpath)
            }
        }
    }
 var didPressButton: ((UITableViewCell) -> Void)

class MyCell: UITableViewCell {

    @IBAction private func myAction(_ sender: UIButton){
        didPressButton(self)
    }
}

注意:-如果你想获取indexPath,你可以使用它并重复上述步骤UICollectionViewUICollectionView extension

extension UICollectionView {
    func returnIndexPath(cell: UICollectionViewCell) -> IndexPath?{
        guard let indexPath = self.indexPath(for: cell) else {
            return nil
        }
        return indexPath
    }
}
4赞 M Murteza 2/20/2020 #16

非常简单地获取索引路径 swift 4, 5

let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
cell.btn.tag = indexPath.row
cell.btn.addTarget(self, action: "buttonTapped:", forControlEvents: 
UIControlEvents.TouchUpInside)

如何在 Btn 中获取 IndexPath 点击:

func buttonTapped(_ sender: UIButton) {
     print(sender.tag)  
}
0赞 iAleksandr 6/22/2020 #17
// CustomCell.swift

protocol CustomCellDelegate {
    func tapDeleteButton(at cell: CustomCell)
}

class CustomCell: UICollectionViewCell {
    
    var delegate: CustomCellDelegate?
    
    fileprivate let deleteButton: UIButton = {
        let button = UIButton(frame: .zero)
        button.setImage(UIImage(named: "delete"), for: .normal)
        button.addTarget(self, action: #selector(deleteButtonTapped(_:)), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    @objc fileprivate func deleteButtonTapped(_sender: UIButton) {
        delegate?.tapDeleteButton(at: self)
    }
    
}

//  ViewController.swift

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as? CustomCell else {
            fatalError("Unexpected cell instead of CustomCell")
        }
        cell.delegate = self
        return cell
    }

}

extension ViewController: CustomCellDelegate {

    func tapDeleteButton(at cell: CustomCell) {
        // Here we get the indexPath of the cell what we tapped on.
        let indexPath = collectionView.indexPath(for: cell)
    }

}
0赞 mramosch 10/17/2020 #18

对行和部分使用单个标记

有一种简单的方法可以使用标记来同时传输 TableView/CollectionView 的行/项和部分。

cellForRowAtIndexPath 中对 UIView.tag 的 IndexPath 进行编码

buttonForCell.tag = convertIndexPathToTag(with: indexPath)

在目标选择器中解码来自发送方的 IndexPath

    @IBAction func touchUpInsideButton(sender: UIButton, forEvent event: UIEvent) {

        var indexPathForButton = convertTagToIndexPath(from: sender)

    }

编码器解码器:

func convertIndexPathToTag(indexPath: IndexPath) -> Int {
    var tag: Int = indexPath.row + (1_000_000 * indexPath.section)
    
    return tag
}

func convertTagToIndexPath(from sender: UIButton) -> IndexPath {
    var section: Int = Int((Float(sender.tag) / 1_000_000).rounded(.down))
    var row: Int = sender.tag - (1_000_000 * section)

    return IndexPath(row: row, section: section)
}

前提是您在 32 位设备上不需要超过 4294967296 行/项目;-)例如

  • 42949 个部分,100_000 个项目/行
  • 4294 个部分,有 1_000_000 个项目/行 - (如上例所示)
  • 429 个部分,10_000_000 个项目/行

—-

警告:请记住,在 TableView/CollectionView 中删除或插入行/项时,必须在插入/删除点之后重新加载所有行/项,以使按钮的标记号与模型保持同步。

—-

0赞 jqgsninimo 2/23/2021 #19

扩展 UITableView 以创建获取视图索引路径的函数:

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

如何使用:

let row = tableView.indexPath(for: sender)?.row
1赞 Mehdi Ijadnazar 10/7/2021 #20

看来我来晚了一点,但我带来了一些有趣的代码。

如何处理单元格中的按钮点击

为了处理 UITableViewCell 或其子类中的按钮点击,我肯定会建议使用上面介绍的委托模式,以便对 和 .cellviewController

如何查找单元格的索引路径

但是,如果由于其他一些原因,您需要在点击按钮或其中的任何其他 UIView 子类时找到单元格的 indexPath,我建议使用类扩展。这样,您可以稍微实现接口隔离SOLIDify 您的代码。

其他解决方案的问题:

  • 标签:如上所述,当您插入或删除行时,它们很脆弱

  • 使用 superView 属性:它无论如何都不整洁。您应该传递多少层视图才能到达本身或包含 .你最终可能会在你的代码中出现这样的东西,这并不漂亮:celltableView

        let tableView = view.superView.superView.superView.superView
    

我的建议:

第一

创建一个扩展,以获取视图层次结构中的第一个类型。UIRespondersuperViewviewT

extension UIResponder {
    func next<T: UIResponder>(_ type: T.Type) -> T? {
        self.next as? T ?? self.next?.next(type)
    }
}   

这将迭代整个视图层次结构,直到它找到给定类型的视图或它将返回 nil 的层次结构的末尾。

下一个

UITableViewCell 上编写扩展,并使用 next 方法查找单元格所属的 tableView 和单元格的 tableViewindexPath

extension UITableViewCell {
    var tableView: UITableView? {
        return next(UITableView.self)
    }

    var indexPath: IndexPath? {
        return tableView?.indexPathForRow(at: self.center)
        //return tableView?.indexPath(for: self) // Note: This will return nil if the cell is not visible yet
    }
}  

就是这样。整洁而简单。

像这样在您想要的地方使用它。

func buttonTapped(_ sender: UIButton) {
    guard let cell = sender.next(YourCellType.self), let indexPath = cell.indexPath else {
        return
    }
    
    // Use indexPath here
}

评论

0赞 Uday Kumar Eega 11/11/2021
这符合我的需要。谢谢伙计。