J'ai une tableView avec des bulles de chat.
Ces bulles sont raccourcies si le nombre de caractères
est supérieur à 250
Si un utilisateur clique sur une bulle
Si une longue bulle est déjà sélectionnée, mais que l'utilisateur sélectionne une autre bulle, je veux que tableView défile jusqu'à la position de la nouvelle bulle sélectionnée.
Je vais partager une vidéo à ce sujet
Sans ce défilement, le contentOffset reste le même et il semble mauvais.
(Dans la vidéo : à droite)
Vidéo:
Droite: sans le défilement mentionné
Gauche: avec défilement
Voici le problème:
Sur la gauche, vous pouvez remarquer que c'est un problème.
Des cellules fantômes aléatoires apparaissent sans raison.
Parfois, cela gâche même la hauteur de certaines bulles (pas dans la vidéo)
Code:
func bubbleTappedHandler(sender: UITapGestureRecognizer) { let touchPoint = sender.location(in: self.tableView) if let indexPath = tableView.indexPathForRow(at: touchPoint) { if indexPath == currentSelectedIndexPath { // Selected bubble is tapped, deselect it self.selectDeselectBubbles(deselect: indexPath) } else { if (currentSelectedIndexPath != nil){ // Deselect old bubble, select new one self.selectDeselectBubbles(select: indexPath, deselect: currentSelectedIndexPath) } else { // Select bubble self.selectDeselectBubbles(select: indexPath) } } } } func selectDeselectBubbles(select: IndexPath? = nil, deselect: IndexPath? = nil){ var deselectCell : WorldMessageCell? var selectCell : WorldMessageCell? if let deselect = deselect { deselectCell = tableView.cellForRow(at: deselect) as? WorldMessageCell } if let select = select { selectCell = tableView.cellForRow(at: select) as? WorldMessageCell } // Deselect Main if let deselect = deselect, let deselectCell = deselectCell { tableView.deselectRow(at: deselect, animated: false) currentSelectedIndexPath = nil // Update text deselectCell.messageLabel.text = self.dataSource[deselect.row].message.shortened() } // Select Main if let select = select, let selectCell = selectCell { tableView.selectRow(at: select, animated: true, scrollPosition: .none) currentSelectedIndexPath = select // Update text deselectCell.messageLabel.text = self.dataSource[select.row].message.full() } UIView.animate(withDuration: appSettings.defaultAnimationSpeed) { // Deselect Constraint changes if let deselect = deselect, let deselectCell = deselectCell { // Constarint change deselectCell.nickNameButtonTopConstraint.constant = 0 deselectCell.timeLabel.alpha = 0.0 deselectCell.layoutIfNeeded() } // Select Constraint changes if let select = select, let selectCell = selectCell { // Constarint change selectCell.nickNameButtonTopConstraint.constant = 4 selectCell.timeLabel.alpha = 1.0 selectCell.layoutIfNeeded() } } self.tableView.beginUpdates() self.tableView.endUpdates() UIView.animate(withDuration: appSettings.defaultAnimationSpeed) { if let select = select, deselect != nil, self.tableView.cellForRow(at: deselect!) == nil && deselectCell != nil { // If deselected row is not anymore on screen // but was before the collapsing, // then scroll to new selected row self.tableView.scrollToRow(at: select, at: .top, animated: false) } } }
Lien: https://github.com/ krptia / test2
J'ai créé une petite version de mon application, afin que vous puissiez voir et tester quel est mon problème. Je serais très reconnaissant si quelqu'un pouvait aider à résoudre ce problème! : c
5 Réponses :
Essayez d'utiliser cette API willDisplay
sur UITableViewDelegate
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if (indexPath == currentSelectedIndexPath) { // logic to expand your cell // you don't need to animate it since still not visible } }
Remplacez votre code pour bubbleTappedHandler par ceci et exécutez et vérifiez:
func bubbleTappedHandler(sender: UITapGestureRecognizer) { let touchPoint = sender.location(in: self.tableView) if let indexPath = tableView.indexPathForRow(at: touchPoint) { if indexPath == currentSelectedIndexPath { currentSelectedIndexPath = nil tableView.reloadRows(at: [indexPath], with: .automatic) } else { if (currentSelectedIndexPath != nil){ if let prevSelectedIndexPath = currentSelectedIndexPath { currentSelectedIndexPath = indexPath tableView.reloadRows(at: [prevSelectedIndexPath, indexPath], with: .automatic) } } else { currentSelectedIndexPath = indexPath tableView.reloadRows(at: [indexPath], with: .automatic) } } DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { [weak self] in let currentIndexPath = self?.currentSelectedIndexPath ?? indexPath self?.tableView.scrollToRow(at: currentIndexPath, at: .top, animated: false) }) } }
Merci pour la réponse, mais cela ne résout pas le problème: c
L'animation n'est pas fluide, encore plus floue avec la méthode reloadRows que vous avez mentionnée
Vous pouvez utiliser tableView? .scrollToRow . Par exemple
let newIndexPath = IndexPath(row: 0, section: 0) // put your selected indexpath and section here yourTableViewName?.scrollToRow(at: newIndexPath, at: UITableViewScrollPosition.top, animated: true) // give the desired scroll position to tableView
Les positions de défilement disponibles sont .aucun , .top , .middle , .bottom
Ce problème est dû à une petite erreur. Faites simplement ces 2 choses et le problème sera résolu.
Définissez les paramètres rowHeight
et estimeRowHeight
de tableView dans `viewDidLoad
tableView.estimatedRowHeight = 316 // Value taken from your project. This may be something else tableView.rowHeight = UITableView.automaticDimension
Supprimez les méthodes déléguées EstimedHeightForRowAt
et heightForRowAt
de votre code.
J'ai essayé ce que vous avez écrit, mais cela pose toujours le problème. J'ai mis à jour le Github avec votre solution, vérifiez-le: cliquez sur le long, puis faites défiler vers le bas (pour que le long soit toujours visible en haut, mais le plus petit aussi): et cliquez sur le plus petit. C'est étrangement animé
Commençons par définir ce que nous entendons par "sans défilement" - nous voulons dire que les cellules restent plus ou moins les mêmes. Nous voulons donc trouver une cellule que nous voulons être la cellule d'ancrage. D'avant les changements à après les changements, les distances entre le haut de la cellule et le haut de l'écran sont les mêmes.
self.tableView.beginUpdates() self.tableView.endUpdates() self.tableView.layoutSubviews() self.scrollToAnchorPoint()
nous appelons cela avant de commencer à faire des choses.
func scrollToAnchorPoint() { if let indexPath = indexPathAnchorPoint, let offset = offsetAnchorPoint { let rect = self.tableView.rectForRow(at: indexPath) let contentOffset = rect.origin.y - offset self.tableView.setContentOffset(CGPoint.init(x: 0, y: contentOffset), animated: false) } }
Ensuite, nous devons définir le décalage du contenu après avoir effectué nos modifications afin que la cellule revienne là où elle est supposée.
func bubbleTappedHandler(sender: UITapGestureRecognizer) { self.setAnchorPoint() ....
Ensuite, nous l'appelons après avoir fait nos changements.
var indexPathAnchorPoint: IndexPath? var offsetAnchorPoint: CGFloat? func findHighestCellThatStartsInFrame() -> UITableViewCell? { return self.tableView.visibleCells.filter { $0.frame.origin.y >= self.tableView.contentOffset.y }.sorted { $0.frame.origin.y > $1.frame.origin.y }.first } func setAnchorPoint() { self.indexPathAnchorPoint = nil; self.offsetAnchorPoint = nil; if let cell = self.findHighestCellThatStartsInFrame() { self.offsetAnchorPoint = cell.frame.origin.y - self.tableView.contentOffset.y self.indexPathAnchorPoint = self.tableView.indexPath(for: cell) } }
L'animation peut être un peu étrange car il se passe beaucoup de choses en même temps. Nous modifions le décalage du contenu et les tailles de la cellule en même temps, mais si vous placez votre doigt à côté de la première cellule dont le haut est visible, vous verrez qu'elle se termine au bon endroit.
p >
Cela fonctionne très bien! Merci pour votre réponse! Oui, l'animation est un peu étrange mais c'est le meilleur qui puisse être réalisé, merci beaucoup! Une question: la tableView saute pendant une seconde lorsqu'une longue bulle est désélectionnée en haut et qu'une plus petite est sélectionnée en dessous. Est-il possible de réparer le saut en rechargeant simplement la tableView? J'expérimente.
Je ne sais pas. Quelque chose que vous pouvez essayer de ralentir l'animation dans toute l'application afin que vous puissiez voir exactement ce que fait l'animation. UIApplication.shared.keyWindow? .Layer.speed = 0,5
Lorsque vous faites défiler, il redessine les cellules et si vous ne gérez pas les cellules pour stocker l'état dans lequel elles se trouvaient, il dessinera l'état initial.
@carbonr merci pour votre réponse! Que puis-je faire à ce sujet pour le rendre fluide?: C
Une chose est importante ici. Vous utilisez un outil de reconnaissance de gestes pour la détection de sélection. La sélection de cellule est-elle activée sur la tableView et implémentez-vous également la méthode
didSelectRow
deUITableViewDelegate
?@Adeel Selection est activé, mais je n'implémente pas la méthode
didSelectRow
. J'ai créé mon propre "système de sélection": parce que je veux que la sélection ne se produise que lorsque l'utilisateur clique exactement sur la bulle (c'est pourquoi j'ai créé un délégué (pourbubbleTappedHandler
), et pourquoi j'ai le VariablecurrentSelectedIndexPath
. En fait, je n'ai pas besoin d'utilisertableView.selectRow (at: ...)
ettableView.deselectRow (at: ...) < / code>, alors oui, je vais simplement supprimer ces lignes.