激活元素后如何获取indexpath.row?


104

我有一个带有按钮的表格视图,当我点击其中一个按钮时,我想使用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)
}

应该使用IndexPathForSelectedRow()代替点变量吗?还是应该在哪里使用?
文森特

Answers:


164

giorashc的回答几乎可以理解这一点,但是他忽略了细胞具有额外contentView一层这一事实。因此,我们必须更深入一层:

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

let indexPath = itemTable.indexPath(for: cell)

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

以上是一种这样的方法,但不一定是最佳方法。尽管功能正常,但它假定了UITableViewCellApple从未记录过的有关细节,例如视图层次结构。将来可能会对此进行更改,因此上面的代码可能会表现出不可预料的行为。

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

在您的单元格类上持有一个闭包的属性,让按钮的action方法调用它。

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
发现得好。我保证,我是一位称职的开发人员;)-修改了我的答案。
雅各布·金

12
这不是从按钮获取单元格的正确方法。多年来,单元的布局已发生变化,并且这种情况下,类似的代码将无法正常工作。不要使用这种方法。
rmaddy

11
这是一个不好的解决方案。它假定了Apple从未同意的有关UITableViewCells的详细信息。虽然UITableViewCells具有contentView属性,但不能保证contentView的超级视图将始终是Cell。
bpapa

1
@PintuRajput您可以向我描述您的视图层次结构吗?您可能会看到此信息,因为您的按钮不是该单元格内容视图的直接子视图。
Jacob King

2
@ymutlu我完全同意,我确实在回答中说明了这一点。我还提出了一个更强大的解决方案。我将原版保留在原处的原因是,我觉得最好是向其他开发人员展示问题的方法,而不是完全避开它,这对他们没有任何帮助。:)
雅各布·金

61

我解决这类问题的方法是在单元格和表格视图之间使用委托协议。这使您可以将按钮处理程序保留在单元格子类中,从而使您可以将补习动作处理程序分配给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)
}

ViewController.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)")
    }
} 

buttonTapped是委托函数,位于视图控制器中。在我的示例中,someButtonTapped是该单元格中的操作方法
Paulw11 '16

@ paulw11我的单元格中没有成员button在这种方法中点击@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
EI队长v2.0

1
这是一个很好的解决方案(与使用投票查看超级视图的标签相比,当前票数更高的两个解决方案还差得很远),但是感觉好像要添加太多额外的代码。
bpapa

2
这是正确的解决方案,应该是公认的答案。它不会滥用标签属性,不会假定单元格的构造(Apple可以轻松更改),并且在移动单元格或在现有单元格之间添加新单元格时仍然可以工作(无需额外编码)。
机器人猫

1
@ Paulw11我最初以为这是很多代码,但事实证明,它比我以前使用的要多得多。感谢您发布此功能强大的解决方案。
阿德里安

53

更新:获取包含按钮(节和行)的单元格的indexPath:

使用按钮位置

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

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)

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

与其爬入超级视图树以获取指向保存UIButton的单元格的指针,还不如利用Antonio上面提到的button.tag属性来更安全,更可重复地使用该技术,在此答案对此进行了描述,如下所示:

cellForRowAtIndexPath:设置标签属性:

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

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

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

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


5
这是一种不错的方法,我会对其进行投票以使您的代表有所了解,但是唯一的缺点是,如果还需要这样做,它不会提供对indexPath.section的访问。很好的答案!
雅各布·金

谢谢雅各!我感谢代表因果报应。如果您想获得的indexPath.section附加信息indexPath.row(无需将tag属性重置为indexPath.section),则cellForRowAtIndexPath:可以将标记更改为button.tag = indexPath,然后在buttonClicked:函数中使用sender.tag.row和进行访问sender.tag.section
Iron John Bonney

1
这是否是一项新功能,因为我确定我记得标记属性为Int类型,而不是AnyObject类型,除非在swift 2.3中进行了更改?
雅各布·金

@JacobKing你是对的!我的糟糕之处是,我在写评论时完全分开了,并以为标签是AnyObject类型。德普-别管我。如果您可以通过indexPath作为标记,则将很有用……
Iron John Bonney

3
也不是一个好方法。一方面,它仅在具有单个节的表视图中起作用。
bpapa

16

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


@ Paulw11关于使用委托属性设置自定义单元格类型(将消息发送到表视图的消息)的答案是一个不错的方法,但是它需要进行大量工作。

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

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

我创建了UITableView的扩展,该扩展使您可以获取表视图单元格中包含的任何视图的indexPath。Optional如果传入的视图实际上不在表视图单元格之内,它将返回nil。以下是整个扩展源文件。您可以简单地将此文件放入项目中,然后使用所包含的indexPathForView(_:)方法来查找包含任何视图的indexPath。

//
//  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")
  }
}

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

编辑:

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


谢谢,我使用扩展名来获取单元格中textview的indexPath-完美运行。
杰里米·安德鲁斯

9

对于 Swift2.1

我找到了一种方法,希望能有所帮助。

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

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

如果错误运行,这意味着什么?为什么无法在tableView中找到该点是什么原因?
OOProg

这(或类似方法,使用UIView转换方法)应该是公认的答案。不知道为什么它现在是#4,因为它没有对表视图的私有层次结构做出假设,它不使用tag属性(几乎总是一个坏主意),并且不涉及很多额外的代码。
bpapa

9

Swift 4解决方案:

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

cell.myButton.tag = indexPath.row

现在,点击功能或任何其他功能。像这样获取它,并将其保存在本地变量中。

currentCellNumber = (sender.view?.tag)!

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

请享用!


如我的回答所述,这种方法有效,但视图标签却很脆弱。例如,简单的整数标记不适用于分段表视图。(一IndexPath这两个整数)。我的方法将总是工作,而且也没有必要安装一个标签到按钮(或其他可点击查看。)
邓肯ç

6

在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
        }
}

最干净的答案,应该是选中的。唯一需要注意的是,tableView在此答案生效之前,需要先引用出口变量。
10000RubyPools

像魅力一样工作!!
Parthpatel1105 '19

4

快速获取索引路径非常简单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) .  


}

3

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

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

  • 在自定义表格单元格中添加一个'indexPath`属性
  • 初始化它 cellForRowAtIndexPath
  • 将点击处理程序从视图控制器移动到单元实现
  • 使用委托模式来通知视图控制器有关tap事件的信息,并传递索引路径

安东尼奥,我有一个自定义单元,很乐意按照您的方式进行操作。但是,它不起作用。我希望运行“滑动以显示删除按钮”代码,即tableView commitEditingStyle方法。我从mainVC类中删除了该代码,并将其放在customCell类中,但是现在该代码不再起作用。我想念什么?
Dave G

我认为这是获取带有x个部分的单元格的indexPath的最佳方法,但是我不认为MVC方法中需要3和4的项目符号
Edward

2

在看到Paulw11关于使用委托回调的建议之后,我想对此进行详细说明/提出另一个类似的建议。如果您不想使用委托模式,可以按如下所示快速使用闭包:

您的单元格类别:

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.
}

2

我找到了一种非常简单的方法来通过使用Model类来管理tableView和collectionView中的任何单元格,而这是一项完美的工作。

确实,现在有更好的方法来处理此问题。这将有助于管理单元和价值。

这是我的输出(屏幕截图),因此请参见:

在此处输入图片说明

  1. 创建模型类非常简单,请遵循以下步骤。创建一个带有名称的swift类RNCheckedModel,编写如下代码。
class RNCheckedModel: NSObject {

    var is_check = false
    var user_name = ""

    }
  1. 创建您的细胞班
class InviteCell: UITableViewCell {

    @IBOutlet var imgProfileImage: UIImageView!
    @IBOutlet var btnCheck: UIButton!
    @IBOutlet var lblName: UILabel!
    @IBOutlet var lblEmail: UILabel!
    }
  1. 最后在使用UITableView时在UIViewController中使用模型类。
    class RNInviteVC: UIViewController, UITableViewDelegate, UITableViewDataSource {


    @IBOutlet var inviteTableView: UITableView!
    @IBOutlet var btnInvite: UIButton!

    var checkArray : NSMutableArray = NSMutableArray()
    var userName : NSMutableArray = NSMutableArray()

    override func viewDidLoad() {
        super.viewDidLoad()
        btnInvite.layer.borderWidth = 1.5
        btnInvite.layer.cornerRadius = btnInvite.frame.height / 2
        btnInvite.layer.borderColor =  hexColor(hex: "#512DA8").cgColor

        var userName1 =["Olivia","Amelia","Emily","Isla","Ava","Lily","Sophia","Ella","Jessica","Mia","Grace","Evie","Sophie","Poppy","Isabella","Charlotte","Freya","Ruby","Daisy","Alice"]


        self.userName.removeAllObjects()
        for items in userName1 {
           print(items)


            let model = RNCheckedModel()
            model.user_name = items
            model.is_check = false
            self.userName.add(model)
        }
      }
     @IBAction func btnInviteClick(_ sender: Any) {

    }
       func tableView(_ tableView: UITableView, numberOfRowsInSection 
       section: Int) -> Int {
        return userName.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: InviteCell = inviteTableView.dequeueReusableCell(withIdentifier: "InviteCell", for: indexPath) as! InviteCell

        let image = UIImage(named: "ic_unchecked")
        cell.imgProfileImage.layer.borderWidth = 1.0
        cell.imgProfileImage.layer.masksToBounds = false
        cell.imgProfileImage.layer.borderColor = UIColor.white.cgColor
        cell.imgProfileImage.layer.cornerRadius =  cell.imgProfileImage.frame.size.width / 2
        cell.imgProfileImage.clipsToBounds = true

        let model = self.userName[indexPath.row] as! RNCheckedModel
        cell.lblName.text = model.user_name

        if (model.is_check) {
            cell.btnCheck.setImage(UIImage(named: "ic_checked"), for: UIControlState.normal)
        }
        else {
            cell.btnCheck.setImage(UIImage(named: "ic_unchecked"), for: UIControlState.normal)
        }

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

        cell.btnCheck.isUserInteractionEnabled = true

    return cell

    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80

    }

    @objc func btnCheck(_ sender: UIButton) {

        let tag = sender.tag
        let indexPath = IndexPath(row: tag, section: 0)
        let cell: InviteCell = inviteTableView.dequeueReusableCell(withIdentifier: "InviteCell", for: indexPath) as! InviteCell

        let model = self.userName[indexPath.row] as! RNCheckedModel

        if (model.is_check) {

            model.is_check = false
            cell.btnCheck.setImage(UIImage(named: "ic_unchecked"), for: UIControlState.normal)

            checkArray.remove(model.user_name)
            if checkArray.count > 0 {
                btnInvite.setTitle("Invite (\(checkArray.count))", for: .normal)
                print(checkArray.count)
                UIView.performWithoutAnimation {
                    self.view.layoutIfNeeded()
                }
            } else {
                btnInvite.setTitle("Invite", for: .normal)
                UIView.performWithoutAnimation {
                    self.view.layoutIfNeeded()
                }
            }

        }else {

            model.is_check = true
            cell.btnCheck.setImage(UIImage(named: "ic_checked"), for: UIControlState.normal)

            checkArray.add(model.user_name)
            if checkArray.count > 0 {
                btnInvite.setTitle("Invite (\(checkArray.count))", for: .normal)
                UIView.performWithoutAnimation {
                self.view.layoutIfNeeded()
                }
            } else {
                 btnInvite.setTitle("Invite", for: .normal)
            }
        }

        self.inviteTableView.reloadData()
    }

    func hexColor(hex:String) -> UIColor {
        var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()

        if (cString.hasPrefix("#")) {
            cString.remove(at: cString.startIndex)
        }

        if ((cString.count) != 6) {
            return UIColor.gray
        }

        var rgbValue:UInt32 = 0
        Scanner(string: cString).scanHexInt32(&rgbValue)

        return UIColor(
            red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
            blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
            alpha: CGFloat(1.0)
        )
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

     }

1

我使用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);
            }   
        }
    }

1

尝试使用#selector调用IBaction。在cellforrowatindexpath中

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

这样,您可以在方法editButtonPressed中访问indexpath

func editButtonPressed(_ sender: UIButton) {

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

}

最合适的答案
Amalendu Kar

不,当用户添加或删除单元格时,标签将关闭。
koen

1

在我的情况下,我有多个节,而节和行索引都是至关重要的,因此在这种情况下,我只是在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

}

1

斯威夫特4和5

使用协议委托的方法1

例如,您有一个UITableViewCell名称为MyCell

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
    }
}

在您的UIViewController工具中,协议MyCellDelegate

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)
    }
}

注意:-如果要获取UICollectionViewindexPath,可以使用它UICollectionView extension并重复上述步骤

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

0

在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)
}

这是行不通的。该按钮的超级视图将不是该单元格。
rmaddy

这确实有效。您唯一需要注意的是,每个人的视图堆栈都不相同。它可以是sender.superview,sender.superview.superview或sender.superview.superview.superview。但这确实很好。
肖恩

0

有时,按钮可能位于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)
}

0
// 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)
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.