iphone - Cómo saber el número de fila de UITableview
ios uiswitch (10)
Tengo una UITableViewCell
con UISwitch
como vista de accesorios de cada celda. Cuando cambio el valor del interruptor en una celda, ¿cómo puedo saber en qué fila está el interruptor? Necesito el número de fila en el evento de cambio de valor cambiado.
¡Las etiquetas, las subclases o la navegación de jerarquía de vista son demasiado trabajo! . Haz esto en tu método de acción:
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
Funciona con cualquier tipo de vista, tablas de sección múltiple, lo que sea que pueda lanzar, siempre y cuando el origen de su remitente esté dentro del marco de la celda (¡gracias rob!), Que generalmente será el caso.
Y aquí está en una extensión UITableView Swift:
extension UITableView {
func indexPath(for view: UIView) -> IndexPath? {
let location = view.convert(CGPoint.zero, to: self)
return self.indexPathForRow(at: location)
}
}
La respuesta aceptada en esta publicación está perfectamente bien. Me gustaría sugerir a los lectores que el siguiente, derivado de @robmayoff en esta publicación, también está perfectamente bien:
- (NSIndexPath *)indexPathForView:(UIView *)view inTableView:(UITableView *)tableView {
while (view && ![view isKindOfClass:[UITableViewCell class]])
view = view.superview;
UITableViewCell *cell = (UITableViewCell *)view;
return [tableView indexPathForCell:cell];
}
Algunos han afirmado que este enfoque contiene demasiado trabajo computacional debido al ciclo while. La alternativa es convertir el origen de la vista al espacio de coordenadas de la vista de tabla y llamar a indexPathForRowAtPoint:
oculta aún más el trabajo.
Algunos han afirmado que este enfoque no es seguro en relación con los posibles cambios del SDK. De hecho, Apple ya ha cambiado la jerarquía de celdas de tabla de vista una vez, agregando un contentView
a la celda. Este enfoque funciona antes y después de dicho cambio. Siempre que se pueda encontrar antepasados a través de una cadena de superviews (que es tan fundamental como cualquier cosa en UIKit), este es un buen código.
La solución aceptada es un truco ingenioso.
Sin embargo, ¿por qué necesitamos usar hitpoint si podemos utilizar la propiedad de tag
ya disponible en UIView ? Usted diría que la etiqueta puede almacenar solo una fila o sección ya que es una sola Int .
Bueno ... No olvides tus raíces chicos (CS101). Un único Int puede almacenar dos enteros de tamaño dos veces más pequeños. Y aquí hay una extensión para esto:
extension Int {
public init(indexPath: IndexPath) {
var marshalledInt: UInt32 = 0xffffffff
let rowPiece = UInt16(indexPath.row)
let sectionPiece = UInt16(indexPath.section)
marshalledInt = marshalledInt & (UInt32(rowPiece) << 16)
marshalledInt = marshalledInt + UInt32(sectionPiece)
self.init(bitPattern: UInt(marshalledInt))
}
var indexPathRepresentation: IndexPath {
let section = self & 0x0000ffff
let pattern: UInt32 = 0xffff0000
let row = (UInt32(self) & pattern) >> 16
return IndexPath(row: Int(row), section: Int(section))
}
}
En su tableView(_:, cellForRowAt:)
puede:
cell.yourSwitch.tag = Int(indexPath: indexPath)
Y luego, en el controlador de acciones, podrías:
func didToogle(sender: UISwitch){
print(sender.tag.indexPathRepresentation)
}
Sin embargo, tenga en cuenta que es una limitación: la fila y la sección no deben ser más grandes que 65535. (UInt16.max)
Dudo que los índices de TableView sean tan altos, pero en caso de que lo hagan, desafíese e implemente un esquema de empaquetado más eficiente. Digamos que si tenemos una sección muy pequeña, no necesitamos los 16 bits para representar una sección. Podemos tener nuestro diseño int como:
{section area length}{all remaining}[4 BITS: section area length - 1]
es decir, nuestros 4 LSB indican la longitud del área de sección - 1, dado que asignamos al menos 1 bit para una sección. Por lo tanto, en caso de que nuestra sección sea 0, la fila puede ocupar hasta 27 bits ([1] [27] [4]), lo cual definitivamente debería ser suficiente.
No sé sobre las secciones múltiples, pero puedo darte una sección ...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index=indexPath.row;
NSString *string=[[NSString alloc]initWithFormat:@"%ld",(long)index];
}
a partir de esto puede obtener el número de fila y puede guardarlo en la cadena ....
Prefiero usar subvistas, si conoces tu diseño, generalmente es súper simple y 1 línea corta ...
NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
Eso es todo, si está más anidado, agrega más supervistas.
Poco más información:
todo lo que está haciendo es pedir la vista principal y su vista principal, que es la celda. Luego, le está pidiendo a su tabla vista el camino de indexación de esa celda que acaba de obtener.
Si establece la propiedad de tag
en el número de fila (como lo sugieren otras respuestas), debe actualizarla siempre en tableView:cellForRowAtIndexPath:
(porque una celda puede reutilizarse para filas diferentes).
En cambio, cuando necesite el número de fila, puede subir la cadena de superview
desde el UISwitch
(o cualquier otra vista) a UITableViewCell
, y luego a UITableView
, y solicitar a la vista de tabla la ruta de índice de la celda:
static NSIndexPath *indexPathForView(UIView *view) {
while (view && ![view isKindOfClass:[UITableViewCell class]])
view = view.superview;
if (!view)
return nil;
UITableViewCell *cell = (UITableViewCell *)view;
while (view && ![view isKindOfClass:[UITableView class]])
view = view.superview;
if (!view)
return nil;
UITableView *tableView = (UITableView *)view;
return [tableView indexPathForCell:cell];
}
Esto no requiere nada en tableView:cellForRowAtIndexPath:
Un colega sugirió lo siguiente, que hice en una categoría UITableView:
+(UITableViewCell*)findParentCellForSubview:(UIView*)view
{
while (([view isKindOfClass:[UITableViewCell class]] == NO) && ([view superview] != nil))
view = [view superview];
if ([view superview] != nil)
return (UITableViewCell*)view;
return nil;
}
Todavía hackly, pero funciona.
Una forma común de hacerlo es establecer la tag
del control (en su caso, el interruptor) a algo que pueda usarse para identificar la fila o el objeto representado.
Por ejemplo, en tableView:cellForRowAtIndexPath:
establezca la propiedad tag
del switch en indexPath.row
y en su método de acción puede obtener la etiqueta del remitente.
Personalmente, no me gusta este enfoque y prefiero subclasificar UITableViewCell. Además, puede ser una buena idea agregar un "desplazamiento" a la etiqueta para evitar cualquier conflicto con las etiquetas de otras vistas.
Una variante más de usar SuperView. Funciona como categoría para UIView.
- (UITableViewCell *)superCell
{
if (!self.superview) {
return nil;
}
if ([self.superview isKindOfClass:[UITableViewCell class]]) {
return (UITableViewCell *)self.superview;
}
return [self.superview superCell];
}
en cellForRowAtIndexPath:
establezca la propiedad de tag
de su control en indexPath.row