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];
Je sais que je ne connais aucune méthode déléguée qui soit appelée après reloadData
. Et en utilisant afterDelay:2
qui est une sorte de hack peut être trop court ou trop long, alors comment puis-je l'implémenter?
Je travaillais récemment avec reloadData
- reloadData
ne modifie pas le contentOffset
ni ne fait défiler la vue du tableau. Il reste en fait le même si le décalage est inférieur à la nouvelle quantité de données.
J'avais des problèmes avec cela parce que je joue avec le dimensionnement des cellules dans ma méthode cellForRowAtIndexPath. J'ai remarqué que les informations de dimensionnement étaient désactivées après avoir fait reloadData, alors j'ai réalisé que je devais le forcer à la mise en page immédiatement avant de rétablir le décalage de contenu.
CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
L'appel de reloadData
sur la tableView ne modifie pas l'offset de contenu. Cependant, si vous utilisez UITableViewAutomaticDimension
qui a été introduit dans iOS 8, vous pourriez avoir un problème.
En utilisant UITableViewAutomaticDimension
, il faut écrire la méthode déléguée tableView: estimatedHeightForRowAtIndexPath:
et renvoyer UITableViewAutomaticDimension
avec tableView: heightForRowAtIndexPath:
qui renvoie également la même chose.
Pour moi, j'ai eu des problèmes dans iOS 8 lors de l'utilisation de cela. C'est parce que la méthode estimatedHeightForRowAtIndexPath:
la méthode renvoyait des valeurs inexactes même si j'utilisais UITableViewAutomaticDimension
. C'était un problème avec iOS 8 car il n'y avait aucun problème avec les appareils iOS 9.
J'ai résolu ce problème en utilisant un dictionnaire pour stocker la valeur de la hauteur de la cellule et la renvoyer. C'est ce que j'ai fait.
- (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;
}
La vérification de la hauteur existe pour les premiers chargements de page.
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
}
Cette méthode doit être appelée au lieu de reloadData
. Cela peut convenir à des cas spécifiques.
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)
}
Par défaut, reloadData conserve le contentOffset. Cependant, il pourrait être mis à jour si vous avez des valeurs d'estimationRowHeight inexactes.
Si vous implémentez la méthode estimatedHeightForRowAtIndexPath
et que votre estimation n'est pas correcte, vous risquez de vous retrouver dans cette situation.
Pour résoudre ce problème, vous pouvez renvoyer une grande hauteur plus grande que chaque hauteur de cellule dans votre tableView, comme ceci:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 800.f; // if 800 is bigger than every possible value of your cell height.
}
Si vous insérez des données au début de votre tableau dataSource, vous devez modifier contentOffset
comme ceci: 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)
}
@ La réponse de Skywalker a montré la meilleure solution pour le problème de hauteur estimée des cellules. Mais parfois, le problème réside dans un endroit différent.
Parfois, le problème réside dans contentInsets of table view. Si vous effectuez un rechargement des données alors que tableView n'est pas visible à l'écran, vous pouvez faire face à un décalage incorrect une fois que la vue de table apparaît à l'écran.
.
J'ai rencontré ce problème dans iOS 9.1
J'ai eu le même problème mais aucune des réponses suggérées ici n'a fonctionné. Voici comment je l'ai résolu. Sous-classe UITableView
et substitution layoutSubviews
méthode comme ceci:
override func layoutSubviews() {
let offset = contentOffset
super.layoutSubviews()
contentOffset = offset
}