Ich habe eine UITableView
, die mit Zellen variabler Höhe gefüllt ist. Ich möchte, dass die Tabelle nach unten gescrollt wird, wenn die Ansicht in den Sichtbereich verschoben wird.
Ich habe momentan folgende Funktion
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[log count]-1 inSection:0];
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
log ist ein veränderbares Array, das die Objekte enthält, aus denen sich der Inhalt jeder Zelle zusammensetzt.
Der obige Code funktioniert gut in viewDidAppear
. Dies hat jedoch den ungünstigen Nebeneffekt, dass der obere Rand der Tabelle angezeigt wird, wenn die Ansicht zum ersten Mal angezeigt wird, und dann zum unteren Rand springen. Ich würde es vorziehen, wenn der table view
nach unten gescrollt werden könnte, bevor er erscheint.
Ich habe die Bildlaufleiste in viewWillAppear
und viewDidLoad
ausprobiert, aber in beiden Fällen wurden die Daten noch nicht in die Tabelle geladen und beide lösen eine Ausnahme aus.
Jede Anleitung wäre sehr dankbar, auch wenn es nur darum geht, mir zu sagen, was ich habe, ist alles was möglich ist.
Ich glaube, dass der Aufruf von [table setContentOffset:CGPointMake(0, CGFLOAT_MAX)]
das tut, was Sie wollen.
Von Jakobs Antwort , das ist der Code:
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.messagesTableView.contentSize.height > self.messagesTableView.frame.size.height)
{
CGPoint offset = CGPointMake(0, self.messagesTableView.contentSize.height - self.messagesTableView.frame.size.height);
[self.messagesTableView setContentOffset:offset animated:YES];
}
}
Ich denke, der einfachste Weg ist folgender:
if (self.messages.count > 0)
{
[self.tableView
scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messages.count-1
inSection:0]
atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
Swift 3 Version:
if messages.count > 0 {
userDefinedOptionsTableView.scrollToRow(at: IndexPath(item:messages.count-1, section: 0), at: .bottom, animated: true)
}
Wenn Sie zum EXACT-Ende des Inhalts scrollen müssen, können Sie dies folgendermaßen tun:
- (void)scrollToBottom
{
CGFloat yOffset = 0;
if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {
yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
}
[self.tableView setContentOffset:CGPointMake(0, yOffset) animated:NO];
}
Ich verwende Autolayout und keine der Antworten funktionierte für mich. Hier ist meine Lösung, die endlich funktioniert hat:
@property (nonatomic, assign) BOOL shouldScrollToLastRow;
- (void)viewDidLoad {
[super viewDidLoad];
_shouldScrollToLastRow = YES;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// Scroll table view to the last row
if (_shouldScrollToLastRow)
{
_shouldScrollToLastRow = NO;
[self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
}
}
Die akzeptierte Lösung von @JacobRelkin hat bei iOS 7.0 mit Auto Layout nicht funktioniert.
Ich habe eine benutzerdefinierte Unterklasse von UIViewController
und eine Instanzvariable _tableView
als Unteransicht ihrer view
hinzugefügt. Ich habe _tableView
mit Auto Layout positioniert. Ich habe versucht, diese Methode am Ende von viewDidLoad
und sogar in viewWillAppear:
aufzurufen. Weder funktionierte.
Also fügte ich meiner benutzerdefinierten Unterklasse UIViewController
die folgende Methode hinzu.
- (void)tableViewScrollToBottomAnimated:(BOOL)animated {
NSInteger numberOfRows = [_tableView numberOfRowsInSection:0];
if (numberOfRows) {
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfRows-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}
}
Das Aufrufen von [self tableViewScrollToBottomAnimated:NO]
am Ende von viewDidLoad
funktioniert. Leider wird tableView:heightForRowAtIndexPath:
für jede Zelle dreimal aufgerufen.
Hier ist eine Erweiterung, die ich in Swift 2.0 implementiert habe. Diese Funktionen sollten nach dem Laden der tableview
aufgerufen werden:
import UIKit
extension UITableView {
func setOffsetToBottom(animated: Bool) {
self.setContentOffset(CGPointMake(0, self.contentSize.height - self.frame.size.height), animated: true)
}
func scrollToLastRow(animated: Bool) {
if self.numberOfRowsInSection(0) > 0 {
self.scrollToRowAtIndexPath(NSIndexPath(forRow: self.numberOfRowsInSection(0) - 1, inSection: 0), atScrollPosition: .Bottom, animated: animated)
}
}
}
xCode 8.3.2, Swift 3.1
import UIKit
extension UITableView {
func scrollToBottom(animated: Bool) {
let y = contentSize.height - frame.size.height
setContentOffset(CGPoint(x: 0, y: (y<0) ? 0 : y), animated: animated)
}
}
tableView.scrollToBottom(animated: true)
Ein "schneller" Weg, um es in Swift zu tun, ist:
var lastIndex = NSIndexPath(forRow: self.messages.count - 1, inSection: 0)
self.messageTableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
perfekt für mich.
Ich wollte, dass die Tabelle mit dem Ende der Tabelle im Rahmen geladen wird. Ich fand Verwendung
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
funktionierte nicht, da ein Fehler ausgegeben wurde, wenn die Tischhöhe unter der Rahmenhöhe lag. Beachten Sie, dass meine Tabelle nur einen Abschnitt enthält.
Die Lösung, die für mich funktioniert hat, war den folgenden Code in viewWillAppear zu implementieren:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// on the initial cell load scroll to the last row (ie the latest Note)
if (initialLoad==TRUE) {
initialLoad=FALSE;
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
CGPoint offset = CGPointMake(0, (1000000.0));
[self.tableView setContentOffset:offset animated:NO];
}
}
Das BOOL ivar initialLoad wird in viewDidLoad auf TRUE gesetzt.
Für Swift:
if tableView.contentSize.height > tableView.frame.size.height
{
let offset = CGPoint(x: 0, y: tableView.contentSize.height - tableView.frame.size.height)
tableView.setContentOffset(offset, animated: false)
}
Sie sollten stattdessen UITableViewScrollPositionBottom
verwenden.
Für Swift 3 (Xcode 8.1):
override func viewDidAppear(_ animated: Bool) {
let numberOfSections = self.tableView.numberOfSections
let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)
let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
}
Ist natürlich ein Bug . Wahrscheinlich irgendwo in Ihrem Code verwenden Sie table.estimatedRowHeight = value
(zum Beispiel 100). Ersetzen Sie diesen Wert durch den höchsten Wert, von dem Sie denken, dass eine Zeilenhöhe erhalten könnte, zum Beispiel 500 .. Dies sollte das Problem in Kombination mit folgendem Code lösen:
//auto scroll down example
let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
self.table.scrollToRowAtIndexPath(NSIndexPath(forRow: self.Messages.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
})
Nach vielem Fummeln hat das für mich funktioniert:
var viewHasAppeared = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !viewHasAppeared { goToBottom() }
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
viewHasAppeared = true
}
private func goToBottom() {
guard data.count > 0 else { return }
let indexPath = NSIndexPath(forRow: data.count - 1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: false)
tableView.layoutIfNeeded()
}
Es hat sich herausgestellt, dass der Schlüssel nicht scrollToRowAtIndexPath
innerhalb von dispatch_async
ist, wie von manchen vorgeschlagen wurde, aber es folgt einfach ein Aufruf an layoutIfNeeded
.
Ich verstehe das so, dass das Aufrufen der Scroll-Methode im current - Thread gewährleistet, dass der Scroll-Offset sofort eingestellt wird, bevor die Ansicht angezeigt wird. Als ich zum Hauptthread schickte, wurde die Ansicht für einen Moment angezeigt, bevor der Bildlauf wirksam wurde.
(Auch NB benötigen Sie das Flag viewHasAppeared
, da Sie nicht jedes Mal, wenn goToBottom
aufgerufen wird, viewDidLayoutSubviews
möchten. Es wird beispielsweise aufgerufen, wenn sich die Ausrichtung ändert.)
In Swift 3.0
self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), animated: true)
Bei Verwendung der obigen Lösungen wird ein Bildlauf zum Ende Ihrer Tabelle durchgeführt (nur wenn der Tabelleninhalt zuerst geladen wird):
//Scroll to bottom of table
CGSize tableSize = myTableView.contentSize;
[myTableView setContentOffset:CGPointMake(0, tableSize.height)];
Danke Jacob für die Antwort. wirklich hilfreich, wenn jemand mit monotouch c # -Version interessant ist
private void SetScrollPositionDown()
{
if (tblShoppingListItem.ContentSize.Height > tblShoppingListItem.Frame.Size.Height)
{
PointF offset = new PointF(0,
tblShoppingListItem.ContentSize.Height -
tblShoppingListItem.Frame.Size.Height);
tblShoppingListItem.SetContentOffset(offset,true );
}
}
Wenn Sie die Daten vor dem Herunterscrollen asynchron laden müssen, haben Sie folgende Möglichkeiten:
tableView.alpha = 0 // We want animation!
lastMessageShown = false // This is ivar
viewModel.fetch { [unowned self] result in
self.tableView.reloadData()
if !self.lastMessageShown {
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
if self.rowCount > 0 {
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.rowCount, inSection: 0), atScrollPosition: .Bottom, animated: false)
}
UIView.animateWithDuration(0.1) {
self.tableView.alpha = 1
self.lastMessageShown = true // Do it once
}
}
}
}
Funktion an Swift 3 nach unten scrollen
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(false)
//scroll down
if lists.count > 2 {
let numberOfSections = self.tableView.numberOfSections
let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)
let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
}
}
func scrollToBottom() {
let sections = self.chatTableView.numberOfSections
if sections > 0 {
let rows = self.chatTableView.numberOfRows(inSection: sections - 1)
let last = IndexPath(row: rows - 1, section: sections - 1)
DispatchQueue.main.async {
self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
}
}
}
sie sollten hinzufügen
DispatchQueue.main.async {
self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
}
oder es wird nicht nach unten scrollen.
Verwenden Sie diesen einfachen Code zum Scrollen von tableView bottom
NSInteger rows = [tableName numberOfRowsInSection:0];
if(rows > 0) {
[tableName scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows-1 inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
Wenn Sie den Rahmen für die Tabellenansicht programmgesteuert einrichten, stellen Sie sicher, dass Sie den Rahmen richtig eingestellt haben.
In iOS funktionierte das gut für mich
CGFloat height = self.inputTableView.contentSize.height;
if (height > CGRectGetHeight(self.inputTableView.frame)) {
height -= (CGRectGetHeight(self.inputTableView.frame) - CGRectGetHeight(self.navigationController.navigationBar.frame));
}
else {
height = 0;
}
[self.inputTableView setContentOffset:CGPointMake(0, height) animated:animated];
Es muss von viewDidLayoutSubviews
aufgerufen werden.
Sie brauchen keinen Bildlauf, indem Sie diesen Code verwenden:
[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
[self.tableViewInfo scrollRectToVisible: CGRectMake (0, self.tableViewInfo.contentSize.height-self.tableViewInfo.height, self.tableViewInfo.width, self.tableViewInfo.height) animiert: JA];
Die akzeptierte Antwort funktionierte nicht mit meiner Tabelle (Tausende von Zeilen, dynamisches Laden), aber der folgende Code funktioniert:
- (void)scrollToBottom:(id)sender {
if ([self.sections count] > 0) {
NSInteger idx = [self.sections count] - 1;
CGRect sectionRect = [self.tableView rectForSection:idx];
sectionRect.size.height = self.tableView.frame.size.height;
[self.tableView scrollRectToVisible:sectionRect animated:NO];
}
}
In Swift brauchst du nur
self.tableView.scrollToNearestSelectedRowAtScrollPosition(UITableViewScrollPosition.Bottom, animated: true)
um es automatisch nach unten zu scrollen
In Swift 3.0 Wenn Sie eine bestimmte Zelle der Tableview aufrufen möchten, ändern Sie den Zellindex.
self.yourTable.reloadData()
self.scrollToBottom()
func scrollToBottom(){
DispatchQueue.global(qos: .background).async {
let indexPath = IndexPath(row: self.yourArr.count-1, section: 0)
self.tblComment.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
Ich habe einen anderen guten Weg gefunden, wie folgt:
func yourFunc() {
//tableView.reloadData()
self.perform(#selector(scrollToBottom), with: nil, afterDelay: 0.1)
}
@objc func scrollToBottom() {
self.tableView.scrollToRow(at: IndexPath(row: 0, section: self.mesArr.count - 1), at: .bottom, animated: false)
}
Am sichersten scrollen Sie am unteren Rand von tableView mit "tableView.scrollRectToVisible". Verwenden Sie nach dem Aufruf von tableView.reloadData (). In Swift 5
private func scrollAtTheBottom() {
let lastIndexPath = IndexPath(row: state.myItemsArray.count - 1, section: 0)
let lastCellPosition = tableView.rectForRow(at: lastIndexPath)
tableView.scrollRectToVisible(lastCellPosition, animated: true)
}
Ich glaube, dass alte Lösungen mit Swift3 nicht funktionieren.
Wenn Sie Zahlenzeilen in der Tabelle kennen, können Sie Folgendes verwenden:
tableView.scrollToRow(
at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ),
at: .top,
animated: true)
Unter iOS 12 scheint die akzeptierte Antwort fehlerhaft zu sein. Ich hatte es mit der folgenden Erweiterung arbeiten
import UIKit
extension UITableView {
public func scrollToBottom(animated: Bool = true) {
guard let dataSource = dataSource else {
return
}
let sections = dataSource.numberOfSections?(in: self) ?? 1
let rows = dataSource.tableView(self, numberOfRowsInSection: sections-1)
let bottomIndex = IndexPath(item: rows - 1, section: sections - 1)
scrollToRow(at: bottomIndex,
at: .bottom,
animated: animated)
}
}