web-dev-qa-db-de.com

So scrollen Sie zum Ende eines UITableView auf dem iPhone, bevor die Ansicht angezeigt wird

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.

124
acqu13sce

Ich glaube, dass der Aufruf von [table setContentOffset:CGPointMake(0, CGFLOAT_MAX)] das tut, was Sie wollen.

142
Jacob Relkin

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];
    }
}
119
Osama F

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)
}
118

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];
}
41
Hans One

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)];
    }
}
31
RaffAl

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.

23
ma11hew28

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)
        }
    }
}
22
Ryan Herubin

Einzelheiten

xCode 8.3.2, Swift 3.1

Code

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)
    }
}

Verwendungszweck

tableView.scrollToBottom(animated: true)
14

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.

12
XcodeNOOB

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.

9
T.J.

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)
        }
8
Segev

Sie sollten stattdessen UITableViewScrollPositionBottom verwenden.

5
Erphan Rajput

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)
}
5
Irshad Qureshi

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)
})
4
Mohsen Karbassi

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.)

3
skot

In Swift 3.0

self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), animated: true)
3
Amit Verma

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)];
3
JimmyJammed

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 );
        }

    }
2
Mahesh

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
            }
        }
    }
}
2
SoftDesigner

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)
        }
    }
2
Tarik
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.

2
user515060

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];
            }
2
Bibin Joseph

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.

1
Josip B.

Sie brauchen keinen Bildlauf, indem Sie diesen Code verwenden:

[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
1
Anuj Kumar Rai

[self.tableViewInfo scrollRectToVisible: CGRectMake (0, self.tableViewInfo.contentSize.height-self.tableViewInfo.height, self.tableViewInfo.width, self.tableViewInfo.height) animiert: JA];

1
leetvin

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];
    }
}
1
user3246173

In Swift brauchst du nur

self.tableView.scrollToNearestSelectedRowAtScrollPosition(UITableViewScrollPosition.Bottom, animated: true)

um es automatisch nach unten zu scrollen

0
Even Cheng

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)
    }
}
0
Amit Verma

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)
}
0
RateRebriduo

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)
}
0
Doca

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)
0
ymutlu

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)
    }
}
0
gkaimakas