ios - Detectando el uibutton en tableview: Mejores prácticas de Swift
uitableview (9)
Como seguimiento de los comentarios de @Lyndsey y @ longbow, noté que cuando tenía el segue en el guión gráfico yendo del botón al destinationVC, se llamaba a prepareForSegue antes de que la función buttonClicked pudiera actualizar la variable urlPath. Para resolver esto, configuré el segue directamente desde el primer VC al destinationVC, y tuve el segue realizado programáticamente después de que se ejecutó el código en el botónClicked. Tal vez no es ideal, pero parece estar funcionando.
func buttonClicked(sender:UIButton) {
let studentDic = tableData[sender.tag] as NSDictionary
let studentIDforTherapyInt = studentDic["ID"] as Int
studentIDforTherapy = String(studentIDforTherapyInt)
urlPath = "BaseURL..."+studentIDforTherapy
self.performSegueWithIdentifier("selectTherapySegue", sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "selectTherapySegue") {
let svc = segue.destinationViewController as SelectTherapyViewController;
svc.urlPath = urlPath
}
Tengo una vista de tabla con un número variable de celdas que representan a los estudiantes que corresponden a su instructor particular. Son células personalizadas con un botón que desencadena una transición a un nuevo CV, generando información detallada sobre el estudiante de cuya celda se trataba. Mi pregunta es:
¿Cuál es la mejor práctica rápida para identificar qué botón se presionó?
Una vez que conozco la ruta del índice, puedo identificar qué información del estudiante necesita pasar al siguiente CV. Hay una gran respuesta para el objetivo C en la publicación a continuación, pero no estoy seguro de cómo traducir a Swift. Cualquier ayuda sería muy apreciada.
Iba a usar el enfoque indexPath hasta que llegué a entender que no sería confiable / incorrecto en algunas situaciones (por ejemplo, eliminar o mover la celda).
Lo que hice es más simple. Por ejemplo, estoy mostrando una serie de colores y sus valores RGB, uno por celda de vista de tabla. Cada color se define en una matriz de estructuras de color. Para mayor claridad, estos son:
struct ColorStruct {
var colorname:String = ""
var red: Int = 0
var green: Int = 0
var blue: Int = 0
}
var colors:[ColorStruct] = [] // The color array
Mi prototipo de celda tiene una var para mantener el índice / clave real en mi matriz:
class allListsCell: UITableViewCell {
@IBOutlet var cellColorView: UIView!
@IBOutlet var cellColorname: UILabel!
var colorIndex = Int() // ---> points directly back to colors[]
@IBAction func colorEditButton(_ sender: UIButton, forEvent event: UIEvent) {
print("colorEditButton: colors[] index:/(self.colorIndex), /(colors[self.colorIndex].colorname)")
}
}
Esta solución toma tres líneas de código, una en la definición de celda prototipo , la segunda en la lógica que puebla una nueva celda, y la tercera en la función IBAction que se llama cuando se presiona el botón de cualquier celda. Debido a que efectivamente he ocultado la "clave" (índice) a los datos en cada celda AS estoy poblando esa nueva celda, no se requiere cálculo, y si mueve las celdas no hay necesidad de actualizar nada.
Lo estoy haciendo a través de prepareforSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.tableView.indexPathForSelectedRow()
let item = tableViewCollection[indexPath!.row].id
let controller = segue.destinationViewController as? DetailVC
controller?.thisItem = item
}
y en el siguiente controlador solo voy a volver a cargar las propiedades completas del elemento, conociendo su id y configurándolo en var thisItem en DetailVC
Nunca es una buena idea usar etiquetas para identificar celdas e indexPaths, eventualmente terminarás con un indexPath incorrecto y consecuentemente la celda e información incorrecta.
Te sugiero que pruebes el siguiente código (Trabajar con UICollectionView, no lo probé con un TableView, pero probablemente funcione bien):
SWIFT 4
@objc func buttonClicked(_ sender: UIButton) {
if let tableView = tableViewNameObj {
let point = tableView.convert(sender.center, from: sender.superview!)
if let wantedIndexPath = tableView.indexPathForItem(at: point) {
let cell = tableView.cellForItem(at: wantedIndexPath) as! SpecificTableViewCell
}
}
}
Otra posible solución sería usar dispatch_block_t
. Si lo haces con Storyboard, primero tienes que crear una variable miembro en tu clase UITableViewCell
personalizada.
var tapBlock: dispatch_block_t?
Luego debes crear un IBAction
y llamar a tapBlock
.
@IBAction func didTouchButton(sender: AnyObject) {
if let tapBlock = self.tapBlock {
tapBlock()
}
}
En su controlador de vista con UITableView
simplemente puede reaccionar al botón de eventos como este
let cell = tableView.dequeueReusableCellWithIdentifier("YourCellIdentifier", forIndexPath: indexPath) as! YourCustomTableViewCell
cell.tapBlock = {
println("Button tapped")
}
Sin embargo, debe tener en cuenta al acceder a self
dentro del bloque, para no crear un ciclo de retención. Asegúrese de acceder a él como [weak self]
.
Swift 3
@ cellForRowAt indexPath
cell.Btn.addTarget(self, action: #selector(self.BtnAction(_:)), for: .touchUpInside)
Entonces
func BtnAction(_ sender: Any)
{
let btn = sender as? UIButton
}
Actualizado para Swift 3
Si lo único que desea hacer es activar un segue en un toque, sería contrario a las mejores prácticas hacerlo a través de un UIButton. Simplemente puede usar el controlador integrado de UIKit para seleccionar una celda, es decir, func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
. Puede implementarlo haciendo algo como lo siguiente:
Crear una UITableViewCell personalizada
class StudentCell: UITableViewCell {
// Declare properties you need for a student in a custom cell.
var student: SuperSpecialStudentObject!
// Other code here...
}
Cuando cargue su UITableView, pase los datos a la celda desde su modelo de datos:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "StudentCell", for: indexPath) as! StudentCell
cell.student = superSpecialDataSource[indexPath.row]
return cell
}
Luego use didSelectRow atIndexPath
para detectar cuándo se ha seleccionado una celda, acceder a la celda y sus datos, y pasar el valor como parámetro para realizar la performSegue
.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! StudentCell
if let dataToSend = cell.student {
performSegue(withIdentifier: "DestinationView", sender: dataToSend)
}
}
Y finalmente en prepareForSegue
:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DestinationView" {
let destination = segue.destination as! DestinationViewController
if let dataToSend = sender as? SuperSpecialStudentObject {
destination.student = dataToSend
}
}
}
Alternativamente, si desea que solo seleccionen una parte de la celda en lugar de tocar en cualquier lugar dentro de la celda, puede agregar un elemento accesorio a la celda, como el elemento accesorio detallado (se parece al círculo con una "i" dentro de ) y utilice el override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
lugar.
Solución Swift 3.0
cell.btnRequest.tag = indexPath.row
cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside)
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}
Si su código lo permite, le recomiendo que configure la etiqueta UIButton
igual a indexPath.row
, de modo que cuando se indexPath.row
su acción, puede extraer la etiqueta y, por lo tanto, remar los datos del botón durante el método desencadenado. Por ejemplo, en cellForRowAtIndexPath
puede establecer la etiqueta:
button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
luego en el buttonClicked:
puedes buscar la etiqueta y, por lo tanto, la fila:
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}
De lo contrario, si eso no es propicio para su código por alguna razón, la traducción de Swift de este Objective-C responde que está vinculado a :
- (void)checkButtonTapped:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
...
}
}
es:
func checkButtonTapped(sender:AnyObject) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
...
}
}