web-dev-qa-db-fra.com

Comment faire défiler le bas d'une UITableView sur l'iPhone avant que la vue ne s'affiche

J'ai une UITableView qui est peuplée de cellules de hauteur variable. Je voudrais que le tableau défile vers le bas lorsque la vue est poussée dans la vue.

J'ai actuellement la fonction suivante

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[log count]-1 inSection:0];
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

log est un tableau mutable contenant les objets qui composent le contenu de chaque cellule.

Le code ci-dessus fonctionne bien dans viewDidAppear, mais il a malheureusement pour effet secondaire d’afficher le haut de la table lorsque la vue apparaît pour la première fois, puis de sauter en bas. Je préférerais que le table view puisse être déplacé vers le bas avant son affichage.

J'ai essayé le parchemin dans viewWillAppear et viewDidLoad mais dans les deux cas, les données n'ont pas encore été chargées dans la table et les deux lèvent une exception.

Toute orientation serait très appréciée, même s’il s’agit simplement de me dire que ce que j’ai, c’est tout ce qui est possible.

124
acqu13sce

Je crois que l'appel de [table setContentOffset:CGPointMake(0, CGFLOAT_MAX)] fera ce que vous voulez.

142
Jacob Relkin

De réponse de Jacob , voici le 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

Je pense que le moyen le plus simple est le suivant:

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
Chamira Fernando

Si vous avez besoin de faire défiler jusqu'à la fin EXACT du contenu, vous pouvez le faire comme ceci:

- (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

J'utilise l'autolayout et aucune des réponses n'a fonctionné pour moi. Voici ma solution qui a finalement fonctionné:

@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

La solution acceptée par @JacobRelkin ne fonctionnait pas pour moi dans iOS 7.0 avec la mise en page automatique.

J'ai une sous-classe personnalisée de UIViewController et ajouté une variable d'instance _tableView en tant que sous-vue de sa view. J'ai positionné _tableView à l'aide de la disposition automatique. J'ai essayé d'appeler cette méthode à la fin de viewDidLoad et même dans viewWillAppear:. Ni travaillé. 

J'ai donc ajouté la méthode suivante à ma sous-classe personnalisée de UIViewController.

- (void)tableViewScrollToBottomAnimated:(BOOL)animated {
    NSInteger numberOfRows = [_tableView numberOfRowsInSection:0];
    if (numberOfRows) {
        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfRows-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

Appeler [self tableViewScrollToBottomAnimated:NO] à la fin de viewDidLoad fonctionne. Malheureusement, tableView:heightForRowAtIndexPath: est également appelé trois fois pour chaque cellule.

23
ma11hew28

Voici une extension que j'ai implémentée dans Swift 2.0. Ces fonctions doivent être appelées après le chargement de la tableview:

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

Détails

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

Usage

tableView.scrollToBottom(animated: true)
14

En fait, une manière plus rapide de le faire à Swift est la suivante:

var lastIndex = NSIndexPath(forRow: self.messages.count - 1, inSection: 0)
            self.messageTableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)

travail parfait pour moi.

12
XcodeNOOB

Je voulais que la table soit chargée avec la fin de la table indiquée dans le cadre. J'ai trouvé en utilisant

NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

ne fonctionnait pas car il y avait une erreur lorsque la hauteur de la table était inférieure à la hauteur du cadre. Notez que mon tableau n'a qu'une section.

La solution qui a fonctionné pour moi a été d'implémenter le code suivant dans viewWillAppear:

- (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];
    }
}

BOOL ivar initialLoad est défini sur TRUE dans viewDidLoad.

9
T.J.

Pour 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

Vous devriez utiliser UITableViewScrollPositionBottom à la place.

5
Erphan Rajput

Pour 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

C’est bien sûr un Bug . Probablement quelque part dans votre code vous utilisez table.estimatedRowHeight = value (par exemple 100). Remplacez cette valeur par la valeur la plus élevée que vous pensez qu'une hauteur de ligne peut obtenir , par exemple 500 .. Cela devrait résoudre le problème en combinaison avec le code suivant:

//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

Après beaucoup de tripes, voici ce qui a fonctionné pour moi:

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

La clé s’est avérée être pas wrapping scrollToRowAtIndexPath à l’intérieur de dispatch_async comme certains l’ont suggéré, mais simplement en la suivant avec un appel à layoutIfNeeded.

Si je comprends bien, appeler la méthode de défilement dans le fil actuel garantit que le décalage de défilement est défini immédiatement, avant la vue est affichée. Lorsque je distribuais sur le thread principal, l'affichage était affiché pendant un instant avant que le défilement ne prenne effet.

(Aussi NB vous avez besoin de l'indicateur viewHasAppeared parce que vous ne voulez pas goToBottom à chaque appel de viewDidLayoutSubviews. Il est appelé par exemple lorsque l'orientation change.)

3
skot

Dans Swift 3.0

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

En utilisant les solutions ci-dessus, cela fera défiler le bas de votre tableau (uniquement si le contenu du tableau est chargé en premier):

//Scroll to bottom of table
CGSize tableSize = myTableView.contentSize;
[myTableView setContentOffset:CGPointMake(0, tableSize.height)];
3
JimmyJammed

Merci Jacob pour la réponse. vraiment utile si quelqu'un intéressant avec monotouch version c # 

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

Si vous devez charger les données de manière asynchrone avant le défilement, voici la solution possible:

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

Fonction sur Swift 3 défiler vers le bas

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

vous devriez ajouter 

DispatchQueue.main.async {
            self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
        }

ou il ne fera pas défiler vers le bas.

2
user515060

Utilisez ce code simple pour faire défiler 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

Si vous configurez un cadre pour tableview par programme, assurez-vous de le définir correctement.

1

Dans iOS cela a bien fonctionné pour moi

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];

Il doit être appelé de viewDidLayoutSubviews

1
Josip B.

Pas besoin de faire défiler, vous pouvez simplement le faire en utilisant ce code:

[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) animé: YES];

1
leetvin

La réponse acceptée ne fonctionnait pas avec ma table (milliers de lignes, chargement dynamique) mais le code ci-dessous fonctionne:

- (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

Dans Swift, vous avez juste besoin

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

pour le faire défiler automatiquement vers le bas

0
Even Cheng

Dans Swift 3.0 Si vous souhaitez accéder à une cellule particulière de la vue tableau, changez la valeur de l'index d'une cellule, comme changer la valeur "self.yourArr.count".

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

J'ai trouvé un autre bon moyen, comme suit:

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

Le moyen le plus sûr de faire défiler la partie inférieure de uitableView est d’utiliser "tableView.scrollRectToVisible". Utiliser après avoir appelé tableView.reloadData (). Dans 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

Je crois que les anciennes solutions ne fonctionnent pas avec Swift3. 

Si vous connaissez un nombre de lignes dans la table, vous pouvez utiliser: 

tableView.scrollToRow(
    at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ), 
    at: .top, 
    animated: true)
0
ymutlu

Sur iOS 12, la réponse acceptée semble être brisée. Je l'ai eu avec l'extension suivante



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