swift button touch nsindexpath

swift - rápido: ¿cómo obtener indexpath.row cuando se toca un botón en una celda?



button touch (14)

Solución Swift 4:

Tiene un botón (myButton) o cualquier otra vista en la celda. Asignar etiqueta en cellForRowAt así

cell.myButton.tag = indexPath.row

Ahora en usted toque Función o cualquier otro. Recógelo así y guárdalo en una variable local.

currentCellNumber = (sender.view?.tag)!

Después de esto, puede usar en cualquier lugar este currentCellNumber para obtener el indexPath.row del botón seleccionado.

¡Disfrutar!

Tengo una vista de tabla con botones y quiero usar indexpath.row cuando se toca uno de ellos. Esto es lo que tengo actualmente, pero siempre es 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) }


Use una extensión de UITableView para obtener la celda para cualquier vista:

La respuesta de @ Paulw11 de configurar un tipo de celda personalizado con una propiedad delegada que envía mensajes a la vista de tabla es una buena manera de hacerlo, pero requiere una cierta cantidad de trabajo para configurarlo.

Creo que recorrer la jerarquía de vistas de la celda de vista de tabla buscando la celda es una mala idea. Es frágil: si luego encierra su botón en una vista para fines de diseño, es probable que ese código se rompa.

Usar etiquetas de vista también es frágil. Debe recordar configurar las etiquetas cuando cree la celda, y si usa ese enfoque en un controlador de vista que usa etiquetas de vista para otro propósito, puede tener números de etiqueta duplicados y su código puede no funcionar como se espera.

He creado una extensión para UITableView que le permite obtener indexPath para cualquier vista contenida en una celda de vista de tabla. Devuelve un Optional que será nulo si la vista pasada en realidad no cae dentro de una celda de vista de tabla. A continuación se muestra el archivo fuente de extensión en su totalidad. Simplemente puede poner este archivo en su proyecto y luego usar el método indexPathForView(_:) incluido para encontrar el indexPath que contiene cualquier vista.

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

Para usarlo, simplemente puede llamar al método en IBAction para un botón que está contenido en una celda:

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

(Tenga en cuenta que la función indexPathForView(_:) solo funcionará si el objeto de vista que se pasa está contenido en una celda que está actualmente en pantalla. Eso es razonable, ya que una vista que no está en pantalla no pertenece realmente a un objeto específico indexPath; es probable que se asigne a un indexPath diferente cuando contiene una celda que se recicla).

EDITAR:

Puede descargar un proyecto de demostración en funcionamiento que utiliza la extensión anterior de Github: TableViewExtension.git


A veces, el botón puede estar dentro de otra vista de UITableViewCell. En ese caso, superview.superview puede no proporcionar el objeto de celda y, por lo tanto, indexPath será nulo.

En ese caso, debemos seguir buscando la supervista hasta que obtengamos el objeto de celda.

Función para obtener objeto de celda por supervista

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 }

Ahora podemos obtener indexPath al tocar el botón como se muestra a continuación

@IBAction func tapButton(_ sender: UIButton) { let cell = getCellForView(view: sender) let indexPath = myTabelView.indexPath(for: cell) }


Dado que el remitente del controlador de eventos es el botón en sí, usaría la propiedad de tag del botón para almacenar el índice, inicializado en cellForRowAtIndexPath .

Pero con un poco más de trabajo lo haría de una manera completamente diferente. Si está utilizando una celda personalizada, así es como abordaría el problema:

  • agregar una propiedad ''indexPath` a la celda de la tabla personalizada
  • Inicializarlo en cellForRowAtIndexPath
  • mover el controlador de tap del controlador de vista a la implementación de la celda
  • use el patrón de delegación para notificar al controlador de vista sobre el evento de tap, pasando la ruta del índice

Después de ver la sugerencia de Paulw11 de usar una devolución de llamada de delegado, quise elaborarlo un poco / presentar otra sugerencia similar. Si no desea utilizar el patrón de delegado, puede utilizar cierres de la siguiente manera:

Tu clase celular:

class Cell: UITableViewCell { @IBOutlet var button: UIButton! var buttonAction: ((sender: AnyObject) -> Void)? @IBAction func buttonPressed(sender: AnyObject) { self.buttonAction?(sender) } }

Su método 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. }


En Swift 3. También se usaron declaraciones de guardia, evitando una larga cadena de llaves.

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


En Swift 4, solo usa esto:

func buttonTapped(_ sender: UIButton) { let buttonPostion = sender.convert(sender.bounds.origin, to: tableView) if let indexPath = tableView.indexPathForRow(at: buttonPostion) { let rowIndex = indexPath.row } }


Intente usar #selector para llamar a IBaction. En el cellforrowatindexpath

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

De esta forma puede acceder a la ruta de acceso indexada dentro del método editButtonPressed

func editButtonPressed(_ sender: UIButton) { print(sender.tag)//this value will be same as indexpath.row }


Mi enfoque para este tipo de problema es usar un protocolo delegado entre la celda y la vista de tabla. Esto le permite mantener el controlador de botones en la subclase de celda, lo que le permite asignar el controlador de acción de retoque a la celda prototipo en Interface Builder, mientras mantiene la lógica del controlador de botones en el controlador de vista.

También evita el enfoque potencialmente frágil de navegar por la jerarquía de vistas o el uso de la propiedad de tag , que tiene problemas cuando cambian los índices de las celdas (como resultado de la inserción, eliminación o reordenamiento)

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


Para Swift2.1

Encontré una manera de hacerlo, con suerte, me ayudará.

let point = tableView.convertPoint(CGPoint.zero, fromView: sender) guard let indexPath = tableView.indexPathForRowAtPoint(point) else { fatalError("can''t find point in tableView") }


Usé el método convertPoint para obtener un punto de tableview y pasar este punto al método indexPathForRowAtPoint para obtener 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); } } }


giorashc casi lo tuvo con su respuesta, pero pasó por alto el hecho de que las celdas tienen una capa contentView adicional. Por lo tanto, tenemos que ir una capa más profundo:

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

Esto se debe a que dentro de la jerarquía de vistas, una vista de tabla tiene celdas como subvistas que posteriormente tienen sus propias ''vistas de contenido'', por eso debe obtener la supervista de esta vista de contenido para obtener la celda en sí. Como resultado de esto, si su botón está contenido en una subvista en lugar de estar directamente en la vista de contenido de la celda, tendrá que ir más allá de las capas para acceder.

Lo anterior es uno de esos enfoques, pero no necesariamente el mejor enfoque. Si bien es funcional, asume detalles sobre un UITableViewCell que Apple nunca ha documentado necesariamente, como su jerarquía de vistas. Esto podría cambiarse en el futuro y, como resultado, el código anterior puede comportarse de manera impredecible.

Como resultado de lo anterior, por razones de longevidad y confiabilidad, recomiendo adoptar otro enfoque. Hay muchas alternativas enumeradas en este hilo, y le animo a que lea, pero mi favorito personal es el siguiente:

Mantenga una propiedad de un cierre en su clase de celda, haga que el método de acción del botón invoque esto.

class MyCell: UITableViewCell { var button: UIButton! var buttonAction: ((Any) -> Void)? @objc func buttonPressed(sender: Any) { self.buttonAction?(sender) } }

Luego, cuando crea su celda en cellForRowAtIndexPath , puede asignar un valor a su cierre.

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

Al mover su código de controlador aquí, puede aprovechar el argumento indexPath ya presente. Este es un enfoque mucho más seguro que el mencionado anteriormente, ya que no se basa en rasgos indocumentados.


ACTUALIZACIÓN : Obteniendo el indexPath de la celda que contiene el botón (tanto la sección como la fila):

Usando la posición del botón

Dentro de su método buttonTapped , puede tomar la posición del botón, convertirlo en una coordenada en la TableView, luego obtener el indexPath de la fila en esa coordenada.

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

NOTA : a veces puede encontrarse con un caso de borde cuando usa la función view.convert(CGPointZero, to:self.tableView) resulta en encontrar nil para una fila en un punto, aunque haya una celda tableView allí. Para solucionar esto, intente pasar una coordenada real que esté ligeramente desplazada del origen, como:

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

Respuesta anterior: Uso de la propiedad de etiqueta (solo devuelve fila)

En lugar de trepar a los árboles de la vista superior para agarrar un puntero a la celda que contiene el UIButton, existe una técnica más segura y más repetible que utiliza la propiedad button.tag mencionada anteriormente por Antonio, descrita en esta respuesta , y que se muestra a continuación:

En cellForRowAtIndexPath: establece la propiedad de etiqueta:

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

Luego, en la función buttonClicked: referencia a esa etiqueta para tomar la fila de indexPath donde se encuentra el botón:

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

Prefiero este método ya que descubrí que balancearse en los árboles de la supervista puede ser una forma arriesgada de diseñar una aplicación. Además, para el objetivo C, he usado esta técnica en el pasado y estoy contento con el resultado.


Encontré una forma muy fácil y eficiente de usar para administrar cualquier celda en tableView y collectionView usando una clase Model y esto funciona perfectamente.

De hecho, hay una manera mucho mejor de manejar esto ahora. Esto funcionará para administrar la celda y el valor

aquí está mi salida (screenshote) así que mira esto

aqui esta mi codigo

  1. Es muy sencillo crear clas de modelo , siga el procedimiento a continuación. Cree una clase rápida con el nombre "RNCheckedModel", escriba el código de la siguiente manera.

clase RNCheckedModel: NSObject {

var is_check = false var user_name = "" }

  1. crea tu clase celular

clase InviteCell: UITableViewCell {

@IBOutlet var imgProfileImage: UIImageView! @IBOutlet var btnCheck: UIButton! @IBOutlet var lblName: UILabel! @IBOutlet var lblEmail: UILabel! }

  1. y finalmente use la clase de modelo en su UIViewController cuando use su UITableView .

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