swift:如何在单元格中的按钮被点击时获取indexpath.row?

Vin*_*ent 87 button touch nsindexpath swift

我有一个带有按钮的tableview,我想在使用其中一个时使用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)
}
Run Code Online (Sandbox Code Playgroud)

Jac*_*ing 139

giorashc差不多用了他的答案,但他忽略了细胞有contentView一层额外的事实.因此,我们必须深入一层:

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

let indexPath = itemTable.indexPath(for: cell)
Run Code Online (Sandbox Code Playgroud)

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

以上是一种这样的方法,但不一定是最好的方法.虽然它是功能性的,但它假定了UITableViewCellApple从未记录过的详细信息,例如它的视图层次结构.这可以在将来进行更改,因此上述代码可能会因此而出现不可预测的行为.

由于上述原因,出于长寿和可靠性的原因,我建议采用另一种方法.这个主题中列出了许多替代方案,我建议您阅读,但我个人最喜欢的内容如下:

在单元类上保存闭包的属性,让按钮的action方法调用它.

class MyCell: UITableViewCell {
    var button: UIButton!

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

    @objc func buttonPressed(sender: Any) {
        self.buttonAction?(sender)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,当您创建单元格时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.
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这不是从按钮获取单元格的正确方法.多年来,单元格的布局发生了变化,这样的代码在发生这种情况时将无法工作.不要使用这种方法. (10认同)
  • 这是一个糟糕的解决方案.它假设有关Apple未曾同意的UITableViewCells的详细信息.虽然UITableViewCells有一个contentView属性,但不能保证contentView的superview永远是Cell. (10认同)
  • 什么是itemTable? (5认同)
  • 好眼力。我是一个称职的开发人员,我保证;) - 修改了我的答案。 (2认同)
  • @ymutlu 我完全同意,我确实在答案中说明了这一点。我还提出了一个更强大的解决方案。我保留原版的原因是因为我觉得用一种方法向其他开发人员展示问题比完全躲避它更好,这不会教他们你看到的任何东西。:) (2认同)

Pau*_*w11 51

我解决这类问题的方法是在单元格和tableview之间使用委托协议.这允许您将按钮处理程序保留在单元子类中,这使您可以将接触操作处理程序分配给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)
}
Run Code Online (Sandbox Code Playgroud)

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)")
    }
} 
Run Code Online (Sandbox Code Playgroud)

  • 这是正确的解决方案,应该是公认的答案.它不会滥用标签属性,不会假设单元格的构造(可以很容易地被Apple更改),并且在移动单元格或在现有单元格之间添加新单元格时仍然可以工作(没有额外的编码). (2认同)

Iro*_*ney 47

更新:获取包含按钮(包括部分和行)的单元格的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)
}
Run Code Online (Sandbox Code Playgroud)

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

let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)
Run Code Online (Sandbox Code Playgroud)

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

而不是爬进上海华树木抢的指针保持所述的UIButton,有利用由Antonio上述button.tag特性的更安全,更可重复的技术,在所述的细胞此答案,并显示如下:

cellForRowAtIndexPath:您设置标签属性:

button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
Run Code Online (Sandbox Code Playgroud)

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

func buttonClicked(sender:UIButton) {
    let buttonRow = sender.tag
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个很好的方法,我会upvote它来让你的rep启动一点,但唯一的缺陷是它不提供访问indexPath.section如果这也是需要的.很棒的答案! (5认同)
  • 也不是一个好方法.首先,它只适用于表视图,其中只有一个部分. (3认同)

Dun*_*n C 13

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


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

我认为走桌子视图单元格的视图层次结构寻找单元格是一个坏主意.它很脆弱 - 如果您稍后将按钮放在视图中以进行布局,则该代码可能会中断.

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

我已经创建了一个UITableView扩展,它允许您获取表视图单元格中包含的任何视图的indexPath.Optional如果传入的视图实际上不属于表视图单元格,则返回nil.以下是完整的扩展源文件.您可以简单地将此文件放在项目中,然后使用included 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
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您只需在IBAction中调用方法,以获取包含在单元格中的按钮:

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

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

编辑:

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


spe*_*ict 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")
    }
Run Code Online (Sandbox Code Playgroud)


Saj*_*Zeb 9

Swift 4解决方案:

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

cell.myButton.tag = indexPath.row
Run Code Online (Sandbox Code Playgroud)

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

currentCellNumber = (sender.view?.tag)!
Run Code Online (Sandbox Code Playgroud)

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

请享用!


小智 5

在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
        }
}
Run Code Online (Sandbox Code Playgroud)