ios - Cómo mantener UIT-OutView contentoffset después de llamar a-reloadData
uitableview (12)
Swift 4 variante de @Skywalker respuesta:
fileprivate var heightDictionary: [Int : CGFloat] = [:]
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
heightDictionary[indexPath.row] = cell.frame.size.height
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let height = heightDictionary[indexPath.row]
return height ?? UITableViewAutomaticDimension
}
Otra solución (extraída de MessageKit):
Este método debe llamarse en lugar de reloadData
. Esto puede adaptarse a casos específicos.
public func reloadDataAndKeepOffset() {
// stop scrolling
setContentOffset(contentOffset, animated: false)
// calculate the offset and reloadData
let beforeContentSize = contentSize
reloadData()
layoutIfNeeded()
let afterContentSize = contentSize
// reset the contentOffset after data is updated
let newOffset = CGPoint(
x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
setContentOffset(newOffset, animated: false)
}
CGPoint offset = [_table contentOffset];
[_table reloadData];
[_table setContentOffset:offset animated:NO]; //unuseful
// __block UITableView *tableBlock = _table;
// [self performBlock:^(id sender) {
// [tableBlock setContentOffset:offset];
// } afterDelay:2];
No conozco ningún método delegado que se llame después de reloadData
. Y afterDelay:2
puede ser demasiado corto o demasiado largo, entonces ¿cómo puedo implementarlo?
Mi inglés no es bueno, espero que puedas entender ...
Estaba teniendo problemas con esto porque me meto con el tamaño de la celda en mi método cellForRowAtIndexPath. Noté que la información del tamaño estaba desactivada después de hacer ReloadData, así que me di cuenta de que necesitaba forzarlo a la distribución inmediatamente antes de volver a configurar el offset de contenido.
CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
Esto está funcionando al 100%
change the tableView.reloadData()
dentro
tableView.reloadRows(at: tableView!.indexPathsForVisibleRows!, with: .none)
Hace poco estuve trabajando con reloadData
- reloadData
no cambia el contentOffset
ni desplaza la vista de tabla. En realidad, permanece igual si el desplazamiento es menor que la nueva cantidad de datos.
La respuesta de @ Skywalker mostró la mejor solución para el problema de la altura estimada de las celdas. Pero a veces el problema está en otro lugar.
A veces, el problema depende del contenidoInsectos de la vista de tabla. Si realiza una recarga de datos mientras TableView no está visible en la pantalla, puede enfrentar una compensación incorrecta después de que aparezca la vista de tabla en la pantalla.
Sucede porque UIViewController puede controlar las inserciones si su scrollView cuando aparece la vista de desplazamiento para permitir la mentira de scrollView debajo de la barra de navegación y la barra de estado transparentes.
Me he enfrentado a este comportamiento en iOS 9.1
Llamar a reloadData
en tableView no cambia el offset del contenido. Sin embargo, si está utilizando UITableViewAutomaticDimension
que se introdujo en iOS 8, podría tener un problema.
Al usar UITableViewAutomaticDimension
, es necesario escribir el método delegado tableView: estimatedHeightForRowAtIndexPath:
y devolver UITableViewAutomaticDimension
junto con tableView: heightForRowAtIndexPath:
que también devuelve lo mismo.
Para mí, tuve problemas en iOS 8 mientras usaba esto. Fue porque el método UITableViewAutomaticDimension
estimatedHeightForRowAtIndexPath:
método devolvía valores inexactos a pesar de que estaba usando UITableViewAutomaticDimension
. Fue un problema con iOS 8 ya que no había problemas con los dispositivos con iOS 9.
Resolví este problema usando un diccionario para almacenar el valor de la altura de la celda y devolverla. Esto es lo que hice.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *key = @(indexPath.row);
NSNumber *height = @(cell.frame.size.height);
[self.cellHeightsDictionary setObject:height forKey:key];
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *key = @(indexPath.row);
NSNumber *height = [self.cellHeightsDictionary objectForKey:key];
if (height)
{
return height.doubleValue;
}
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
El control de si existe altura es la primera vez que se carga la página.
Por defecto, reloadData mantiene el contentOffset. Sin embargo, podría actualizarse si tiene valores estimados incorrectos de RowHeight.
Porque funciona bien
[tView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
Si implementa el método estimatedHeightForRowAtIndexPath
y su estimación no es correcta, es posible que se encuentre en esta situación.
Para resolver esto, puede devolver una altura grande que sea más grande que la altura de cada celda en su tableView, así:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 800.f; // if 800 is bigger than every possible value of your cell height.
}
Si inserta datos al principio de su matriz dataSource, debe cambiar contentOffset
siguiente manera: Swift 3+
func prepareReloadData() {
let previousContentHeight = tableView.contentSize.height
let previousContentOffset = tableView.contentOffset.y
tableView.reloadData()
let currentContentOffset = tableView.contentSize.height - previousContentHeight + previousContentOffset
tableView.contentOffset = CGPoint(x: 0, y: currentContentOffset)
}
Tuve el mismo problema, pero ninguna de las respuestas sugeridas aquí funcionó. Así es como lo resolví. Subclass UITableView
y anula el método de layoutSubviews
como este:
override func layoutSubviews() {
let offset = contentOffset
super.layoutSubviews()
contentOffset = offset
}