swift:当单元格中的button被点击时如何获得indexpath.row?
我有一个button的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) }
giorashc几乎和他的答案一样,但还有一行代码是必需的。 正确的答案是:
Swift 3.0+更简洁:
if let cell = sender.superview?.superview as? CellView { let indexPath = itemTable.indexPath(for: cell) }
更“Swifty”版本:
guard let cell = sender.superview?.superview as? CellView else { return // or fatalError() or whatever } let indexPath = itemTable.indexPath(for: cell) // Rest of function
原始(Swift 2.x /厄运金字塔):
var indexPath: NSIndexPath! if let button = sender as? UIButton { if let superview = button.superview { if let cell = superview.superview as? MyCellClassHere { indexPath = itemTable.indexPathForCell(cell) } } } // Check for indexPath value, if any, here.
这是因为在视图内,tableView有单元格作为子视图,这些子视图有自己的“内容视图”,这就是为什么你必须得到这个内容视图的超视图来获取单元格本身。 因此,如果您的button包含在子视图中,而不是直接存储在单元格的内容视图中,那么您必须深入一层才能访问它。
希望这可以帮助,
雅各
更新 :获取包含button(包括部分和行)的单元格的indexPath:
使用button位置
在你的buttonTapped
方法里面,你可以抓住button的位置,将它转换成tableView中的坐标,然后得到该坐标处的行的indexPath。
func buttonTapped(_ sender:AnyObject) { let buttonPosition:CGPoint = sender.convert(CGPointZero, to:self.tableView) let indexPath = self.tableView.indexPathForRow(at: buttonPosition) }
注意 :有时,当使用函数view.convert(CGPointZero, to:self.tableView)
时,可能会遇到边缘情况,即使在view.convert(CGPointZero, to:self.tableView)
有一个tableView单元,也会导致在某一点findnil
。 为了解决这个问题,尝试传递一个与原点稍微偏移的实际坐标,例如:
let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)
上一个答案:使用标签属性 (只返回行)
而不是爬到superview树上来抓取一个指向UIButton的单元格的指针,而是使用上面Antonio提到的button.tag属性,在这个答案中描述了一个更安全,更可重复的技术,如下所示:
在cellForRowAtIndexPath:
你设置标签属性:
button.tag = indexPath.row button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
然后,在buttonClicked:
函数中,引用该标签来获取button所在的indexPath行:
func buttonClicked(sender:UIButton) { let buttonRow = sender.tag }
我更喜欢这种方法,因为我发现在超视图树中摆动可能是devise应用程序的一种有风险的方式。 此外,对于Objective-C,我过去曾经使用这种技术 ,并对结果感到满意。
我遇到这种问题的方法是在单元格和tableview之间使用委托协议。 这允许您将button处理程序保留在单元格子类中,从而使您可以将触摸操作处理程序分配给Interface Builder中的原型单元,同时仍然将button处理程序逻辑保留在视图控制器中。
它还避免了导航视图层次结构或使用tag
属性的潜在脆弱方法,在单元索引更改(由于插入,删除或重新sorting的结果)
CellSubclass.swift
protocol CellSubclassDelegate: class { func buttonTapped(cell: CellSubclass) } class CellSubclass: UITableViewCell { @IBOutlet var someButton: UIButton! 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)") } }
对于Swift2.1
我find了一个办法,希望这会有所帮助。
let point = tableView.convertPoint(CGPoint.zero, fromView: sender) guard let indexPath = tableView.indexPathForRowAtPoint(point) else { fatalError("can't find point in tableView") }
使用UITableView的扩展来获取任何视图的单元格:
@ Paulw11设置一个自定义单元格types的答案是一个委托属性,它将消息发送到表视图是一个好方法,但这需要一定的工作量来设置。
我认为走表格视图单元格的视图层次结构来寻找单元格是一个坏主意。 这是脆弱的 – 如果你以后把你的button放在一个视图中进行布局,那么这个代码很可能会中断。
使用视图标签也是脆弱的。 您必须记住在创build单元格时设置标签,并且如果您在使用视图标签用于其他目的的视图控制器中使用该方法,那么您可能会有重复的标签号码,并且代码可能无法正常工作。
我已经创build了一个UITableView的扩展,使您可以获取包含在表视图单元格中的任何视图的indexPath。 它返回一个Optional
,如果传入的视图实际上不在表视图单元格内,它将是零。 以下是完整的扩展源文件。 您可以简单地把这个文件放在你的项目中,然后使用包含的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 origin = view.bounds.origin let viewOrigin = self.convert(origin, from: view) let indexPath = self.indexPathForRow(at: viewOrigin) return indexPath } }
要使用它,你可以简单地在IBAction中调用包含在单元格中的button的方法:
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。)
由于事件处理程序的发件人是button本身,我会使用button的tag
属性来存储索引,在cellForRowAtIndexPath
初始化。
但是稍微多一点工作,我会以完全不同的方式做。 如果您正在使用自定义单元格,这是我将如何处理该问题:
- 将“indexPath”属性添加到自定义表格单元格
- 在
cellForRowAtIndexPath
初始化它 - 将tap控制器从视图控制器移动到单元实现
- 使用委托模式通知视图控制器关于轻击事件,传递索引path
我使用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); } } }
在看到Paulw11提出使用委托callback的build议之后,我想稍微解释一下/提出另外一个类似的build议。 如果你不想使用委托模式,你可以在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. }
有时button可能在UITableViewCell的另一个视图内。 在这种情况下,superview.superview可能不会给出单元格对象,因此indexPath将为零。
在这种情况下,我们应该继续find超级观点,直到我们得到细胞对象。
通过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 }
现在我们可以按下button获取indexPath,如下所示
@IBAction func tapButton(_ sender: UIButton) { let cell = getCellForView(view: sender) let indexPath = myTabelView.indexPath(for: cell) }
在Swift 3中也使用了guard语句,避免了一长串大括号。
func buttonTapped(sender: UIButton) { guard let cellInAction = sender.superview as? UITableViewCell else { return } guard let indexPath = tableView?.indexPath(for: cellInAction) else { return } print(indexPath) }