Ich erstelle eine App mit einer Feed-Ansicht für vom Benutzer eingereichte Beiträge. Diese Ansicht hat eine UITableView
mit einer benutzerdefinierten UITableViewCell
Implementierung. In dieser Zelle habe ich eine andere UITableView
zum Anzeigen von Kommentaren. Das Wesentliche ist ungefähr so:
Feed TableView
PostCell
Comments (TableView)
CommentCell
PostCell
Comments (TableView)
CommentCell
CommentCell
CommentCell
CommentCell
CommentCell
Der erste Feed wird mit 3 Kommentaren zur Vorschau heruntergeladen. Wenn jedoch weitere Kommentare vorhanden sind oder der Benutzer einen Kommentar hinzufügt oder löscht, möchte ich den vorhandenen PostCell
Platz in der Feed-Tabellenansicht aktualisieren, indem ich ihn CommentCells
zur Kommentartabelle hinzufüge oder daraus entferne der PostCell
. Ich benutze derzeit den folgenden Helfer, um dies zu erreichen:
// (PostCell.swift) Handle showing/hiding comments
func animateAddOrDeleteComments(startRow: Int, endRow: Int, operation: CellOperation) {
let table = self.superview?.superview as UITableView
// "table" is outer feed table
// self is the PostCell that is updating it's comments
// self.comments is UITableView for displaying comments inside of the PostCell
table.beginUpdates()
self.comments.beginUpdates()
// This function handles inserting/removing/reloading a range of comments
// so we build out an array of index paths for each row that needs updating
var indexPaths = [NSIndexPath]()
for var index = startRow; index <= endRow; index++ {
indexPaths.append(NSIndexPath(forRow: index, inSection: 0))
}
switch operation {
case .INSERT:
self.comments.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
case .DELETE:
self.comments.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
case .RELOAD:
self.comments.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
}
self.comments.endUpdates()
table.endUpdates()
// trigger a call to updateConstraints so that we can update the height constraint
// of the comments table to fit all of the comments
self.setNeedsUpdateConstraints()
}
override func updateConstraints() {
super.updateConstraints()
self.commentsHeight.constant = self.comments.sizeThatFits(UILayoutFittingCompressedSize).height
}
Dies führt das Update einwandfrei durch. Der Beitrag wird an Ort und Stelle aktualisiert, wobei Kommentare PostCell
wie erwartet innerhalb des Beitrags hinzugefügt oder entfernt werden . Ich verwende die automatische Größenanpassung PostCells
in der Feed-Tabelle. Die Kommentartabelle des wird PostCell
erweitert, um alle Kommentare anzuzeigen, aber die Animation ist etwas ruckelig und die Tabelle rollt ungefähr ein Dutzend Pixel auf und ab, während die Zellenaktualisierungsanimation stattfindet.
Das Springen während der Größenänderung ist etwas nervig, aber mein Hauptproblem kommt danach. Wenn ich jetzt im Feed nach unten scrolle, ist das Scrollen wie zuvor reibungslos. Wenn ich jedoch über die Zelle nach oben scrolle, deren Größe ich gerade nach dem Hinzufügen von Kommentaren geändert habe, springt der Feed einige Male zurück, bevor er den oberen Rand des Feeds erreicht. Ich iOS8
richte die Zellen für die automatische Größenanpassung für den Feed folgendermaßen ein:
// (FeedController.swift)
// tableView is the feed table containing PostCells
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 560
Wenn ich das entferne estimatedRowHeight
, wird die Tabelle immer dann nach oben gescrollt, wenn sich die Zellenhöhe ändert. Ich fühle mich jetzt ziemlich festgefahren und könnte als neuer iOS-Entwickler alle Tipps gebrauchen, die Sie haben könnten.
quelle
Wir hatten das gleiche Problem. Dies beruht auf einer schlechten Schätzung der Zellenhöhe, die dazu führt, dass das SDK eine schlechte Höhe erzwingt, die beim Zurückscrollen zum Springen der Zellen führt. Je nachdem, wie Sie Ihre Zelle erstellt haben, können Sie dies am besten beheben, indem Sie die
UITableViewDelegate
Methode implementieren- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
Solange Ihre Schätzung dem tatsächlichen Wert der Zellenhöhe ziemlich nahe kommt, wird das Springen und Ruckeln fast aufgehoben. So haben wir es implementiert, Sie erhalten die Logik:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { // This method will get your cell identifier based on your data NSString *cellType = [self reuseIdentifierForIndexPath:indexPath]; if ([cellType isEqualToString:kFirstCellIdentifier]) return kFirstCellHeight; else if ([cellType isEqualToString:kSecondCellIdentifier]) return kSecondCellHeight; else if ([cellType isEqualToString:kThirdCellIdentifier]) return kThirdCellHeight; else { return UITableViewAutomaticDimension; } }
Swift 2-Unterstützung hinzugefügt
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { // This method will get your cell identifier based on your data let cellType = reuseIdentifierForIndexPath(indexPath) if cellType == kFirstCellIdentifier return kFirstCellHeight else if cellType == kSecondCellIdentifier return kSecondCellHeight else if cellType == kThirdCellIdentifier return kThirdCellHeight else return UITableViewAutomaticDimension }
quelle
heightForRowAtIndexPath
, hatte aber immer noch einen ruckartigenUITableView
Bildlauf. Befolgen Sie die Antwort von @GabrielCartier und fügen Sie je nach Zelltyp eine spezifischere Logik hinzu, um das Problem wirklich zu lösen. Vielen Dank!dosdos Antwort hat bei mir in Swift 2 funktioniert
Erkläre den Ivar
var heightAtIndexPath = NSMutableDictionary()
in func viewDidLoad ()
func viewDidLoad() { .... your code self.tableView.rowHeight = UITableViewAutomaticDimension }
Fügen Sie dann die folgenden 2 Methoden hinzu:
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let height = self.heightAtIndexPath.objectForKey(indexPath) if ((height) != nil) { return CGFloat(height!.floatValue) } else { return UITableViewAutomaticDimension } } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height = cell.frame.size.height self.heightAtIndexPath.setObject(height, forKey: indexPath) }
SWIFT 3:
var heightAtIndexPath = [IndexPath: CGFloat]() func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return self.heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { self.heightAtIndexPath[indexPath] = cell.frame.size.height }
quelle
@dosdos Lösung funktioniert gut
Aber es gibt etwas, das Sie hinzufügen sollten
folgende @dosdos Antwort
Swift 3/4
@IBOutlet var tableView : UITableView! var heightAtIndexPath = NSMutableDictionary() override func viewDidLoad() { super.viewDidLoad() tableView?.rowHeight = UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber { return CGFloat(height.floatValue) } else { return UITableViewAutomaticDimension } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let height = NSNumber(value: Float(cell.frame.size.height)) heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying) }
Verwenden Sie diese Zeilen dann, wann immer Sie möchten. Für mich verwende ich sie in textDidChange
Bewegen Sie sich schließlich nach oben
tableView.reloadData() self.tableView.layoutIfNeeded() self.tableView.setContentOffset(CGPoint.zero, animated: true)
quelle
Ich hatte auch das gleiche Problem. Ich habe eine Problemumgehung gefunden, die den Ruck jedoch nicht vollständig behebt. Aber es scheint viel besser zu sein als beim vorherigen abgehackten Scrollen.
Versuchen Sie in Ihrer
UITableView
Delegatenmethode:cellForRowAtIndexPath:
, die Einschränkungen mithilfe der folgenden beiden Methoden zu aktualisieren, bevor Sie die Zelle zurückgeben. (Schnelle Sprache)BEARBEITEN: Möglicherweise müssen Sie auch mit dem
tableView.estimatedRowHeight
Wert herumspielen, um ein flüssigeres Scrollen zu erzielen.quelle
Folgende @dosdos Antwort.
Ich fand es auch interessant zu implementieren:
tableView(tableView: didEndDisplayingCell: forRowAtIndexPath:
Speziell für meinen Code, bei dem sich die Zelle dynamisch ändert, während die Zelle bereits auf dem Bildschirm angezeigt wird. Das Aktualisieren des Wörterbuchs auf diese Weise hilft beim zweiten Anzeigen der Zelle.
var heightAtIndexPath = [NSIndexPath : NSNumber]() .... tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = UITableViewAutomaticDimension .... extension TableViewViewController: UITableViewDelegate { //MARK: - UITableViewDelegate func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let height = heightAtIndexPath[indexPath] if let height = height { return CGFloat(height) } else { return UITableViewAutomaticDimension } } func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height: NSNumber = CGRectGetHeight(cell.frame) heightAtIndexPath[indexPath] = height } func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height: NSNumber = CGRectGetHeight(cell.frame) heightAtIndexPath[indexPath] = height } }
quelle