ios uitableview ios7

ios - UITableViewCell no se anula cuando se desliza hacia atrás rápidamente



ios7 (15)

He actualizado tres de mis aplicaciones a iOS 7, pero en las tres, a pesar de que no comparten ningún código, tengo el problema de que si el usuario se desliza para regresar al controlador de navegación (en lugar de presionar el botón Atrás) rápidamente , la celda permanecerá en su estado seleccionado.

Para las tres aplicaciones, una usa células personalizadas creadas mediante programación, otra usa celdas personalizadas creadas en un guión gráfico y la tercera usa celdas predeterminadas en una subclase muy básica de UITableView, también en un guión gráfico. En los tres casos, las celdas no se anulan por sí mismas. Si el usuario desliza lentamente o toca el botón Atrás, se desactiva como normal.

Esto solo sucede en mis aplicaciones para iOS 7, las propias aplicaciones de Apple y las aplicaciones de terceros actualizadas para iOS 7 parecen comportarse normalmente (aunque con ligeras diferencias en la rapidez con la que se anula la selección de las celdas).

Debe haber algo que estoy haciendo mal, pero no estoy seguro de qué?


Basado en el código de Rhult , hice algunos cambios.

Esta implementación permite al usuario cancelar el deslizamiento hacia atrás y aún así mantenerse seleccionado para volver a deslizar en el futuro anular la selección de la animación

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath; - (void)viewDidLoad { [super viewDidLoad]; self.clearsSelectionOnViewWillAppear = NO; } -(void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.savedSelectedIndexPath = nil; } -(void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.savedSelectedIndexPath && ![self.tableView indexPathForSelectedRow]) { [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } else { self.savedSelectedIndexPath = [self.tableView indexPathForSelectedRow]; } }


Después de encontrarme con esto hoy, descubrí que aparentemente este es un problema bastante conocido con UITableView, su soporte para transiciones de navegación interactivas está ligeramente roto. La gente detrás de Castro ha publicado un excelente análisis y solución a esto: http://blog.supertop.co/post/80781694515/viewmightappear

Decidí usar su solución que también considera las transiciones canceladas:

- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; NSIndexPath *selectedRowIndexPath = [self.tableView indexPathForSelectedRow]; if (selectedRowIndexPath) { [self.tableView deselectRowAtIndexPath:selectedRowIndexPath animated:YES]; [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) { if ([context isCancelled]) { [self.tableView selectRowAtIndexPath:selectedRowIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } }]; } }


Esta solución anima la anulación de la fila junto con el coordinador de transición (para que un VC dirigido por el usuario descarte) y vuelve a aplicar la selección si el usuario cancela la transición. Adaptado de una solución de Caleb Davenport en Swift. Solo probado en iOS 9. Probado para funcionar tanto con la transición impulsada por el usuario (deslizamiento) como con el botón "Atrás" al estilo antiguo.

En la subclase UITableViewController :

- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Workaround. clearsSelectionOnViewWillAppear is unreliable for user-driven (swipe) VC dismiss NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow; if (indexPath && self.transitionCoordinator) { [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { [self.tableView deselectRowAtIndexPath:indexPath animated:animated]; } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { if ([context isCancelled]) { [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } }]; } }


Esto funcionó mejor para mí:

- (void)viewDidLoad { [super viewDidLoad]; self.clearsSelectionOnViewWillAppear = NO; } -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated]; }

Incluso conseguí desmarcar mucho mejor cuando estaba deslizando hacia atrás lentamente.


Estoy lidiando con el mismo problema ahora mismo. El UICatalog -muestra de Apple parece traer la solución sucia.

Realmente no me hace feliz en absoluto. Como se mencionó anteriormente, usa [self.tableView deselectRowAtIndexPath:tableSelection animated:NO]; para anular la selección de la fila seleccionada actualmente.

- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // this UIViewController is about to re-appear, make sure we remove the current selection in our table view NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow]; [self.tableView deselectRowAtIndexPath:tableSelection animated:NO]; // some over view controller could have changed our nav bar tint color, so reset it here self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor]; }

Tengo que mencionar que el código de muestra puede no ser ios 7 iOS 8 iOS 9 iOS 10 listo

Algo que realmente me confunde es la referencia de clase UITableViewController :

Cuando la vista de tabla está a punto de aparecer la primera vez que se carga, el controlador de vista de tabla vuelve a cargar los datos de la vista de tabla. También borra su selección (con o sin animación, dependiendo de la solicitud) cada vez que se muestra la vista de tabla. La clase UITableViewController implementa esto en el método de superclase viewWillAppear: Puede deshabilitar este comportamiento cambiando el valor en la propiedad clearsSelectionOnViewWillAppear .

Este es exactamente el comportamiento que espero ... pero parece que no funciona. Ni para ti ni para mí. Realmente tenemos que usar la solución "sucia" y hacerlo por nuestra cuenta.


Estoy usando

[tableView deselectRowAtIndexPath:indexPath animated:YES];

al final del método

(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

Me gusta esto:

(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //doing something according to selected cell... [tableView deselectRowAtIndexPath:indexPath animated:YES]; }


He encontrado una solución muy simple a este problema que hace que el comportamiento predeterminado funcione como debería. No estaba satisfecho con las soluciones que implican deselectRowAtIndexPath ya que el efecto visual resultante era ligeramente diferente.

Todo lo que tiene que hacer para evitar este extraño comportamiento es volver a cargar la tabla cuando se muestre la vista:

- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.tableView reloadData]; }


La respuesta de Fabio funciona bien, pero no da la apariencia correcta si el usuario se desliza un poco y luego cambia de opinión. Para que el caso sea el correcto, debe guardar la ruta de índice seleccionada y restablecerla cuando sea necesario.

- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.savedSelectedIndexPath = nil; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.savedSelectedIndexPath) { [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.savedSelectedIndexPath = self.tableView.indexPathForSelectedRow; if (self.savedSelectedIndexPath) { [self.tableView deselectRowAtIndexPath:self.savedSelectedIndexPath animated:YES]; } }

Si usa un UITableViewController, asegúrese de deshabilitar el clearing incorporado:

self.clearsSelectionOnViewWillAppear = NO;

y agregue la propiedad para savedSelectedIndexPath:

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;

Si necesita hacer esto en algunas clases diferentes, puede tener sentido dividirlo en un ayudante, por ejemplo, como lo hice en este momento: https://gist.github.com/rhult/46ee6c4e8a862a8e66d4


Para veloz

override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) guard let indexPath = tableView.indexPathForSelectedRow else{ return } tableView.deselectRowAtIndexPath(indexPath, animated: true) }


Puedes intentar establecer

self.clearsSelectionOnViewWillAppear = YES;

en un UITableViewController o

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:NO];

en viewWillAppear, tal vez antes de llamar a [super viewWillAppear: animated]; Si su UItableView no está dentro de un UITableViewController, debe anular la selección de las celdas manualmente:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; }


Simple Swift 3 Respuesta:

override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if tableView.indexPathForSelectedRow != nil { self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true) } }


Utilizar

[tableView deselectRowAtIndexPath:indexPath animated:YES];

código en

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method


Codestage siempre fue la mejor respuesta , así que decidí convertirlo en Swift 2.

override func viewWillAppear(animated: Bool) { super.viewWillAppear(true) let selectedRowIndexPath = self.tableView.indexPathForSelectedRow if ((selectedRowIndexPath) != nil) { self.tableView.deselectRowAtIndexPath(selectedRowIndexPath!, animated: true) self.transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock({ context in if (context.isCancelled()) { self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.None) } }) } }


La solución de Rhult funciona perfectamente en iOS 9.2. Esta es la implementación en Swift:

Declare una variable en su MasterViewController para guardar IndexPath:

var savedSelectedIndexPath: NSIndexPath?

Luego puede poner el código en una extensión para mayor claridad:

extension MasterViewController { override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) self.savedSelectedIndexPath = nil } override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if let indexPath = self.savedSelectedIndexPath { self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None) } } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) self.savedSelectedIndexPath = tableView.indexPathForSelectedRow if let indexPath = self.savedSelectedIndexPath { self.tableView.deselectRowAtIndexPath(indexPath, animated: true) } } }


Respuesta de Codestage , en Swift 3. notifyWhenInteractionEnds está en desuso.

override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) if let indexPath = self.tableView.indexPathForSelectedRow { self.tableView.deselectRow(at: indexPath, animated: true) self.transitionCoordinator?.notifyWhenInteractionChanges { (context) in if context.isCancelled { self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) } } } }