web-dev-qa-db-fra.com

scrollToRowAtIndexPath avec UITableView ne fonctionne pas

J'ai dans iOS8 une vue de table comme celle-ci:

tableView = UITableView(frame: view.bounds, style: .Plain)
view.addSubview(tableView)

Lorsque l'utilisateur tape et envoie du texte au clavier, l'application applique la méthode suivante pour faire défiler la table. Le but est de voir le nouveau texte à l'écran (comme un chat)

let numberOfRows = tableView.numberOfRowsInSection(0)
        if numberOfRows > 0 {
            let indexPath = NSIndexPath(forRow: numberOfRows-1, inSection: 0)
            tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: animated)
        }

Mais la vue sous forme de tableau ne fait pas défiler jusqu'au démarrage.

Quelqu'un a une solution?

Je vous remercie.

Explication:

Définition de la classe: 

class ChatViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextViewDelegate 

alors j'ai la définition de la vue tableau: 

var tableView: UITableView! 

dans viewDidLoad: 

tableView = UITableView(frame: view.bounds, style: .Plain) 
tableView.dataSource = self 
tableView.delegate = self 
view.addSubview(tableView) 

puis j'ai l'appel du code qui doit faire défiler (deuxième bloc de code sur ma réponse). Lorsque je lance l’application, la vue table doit défiler vers le bas, mais cela ne fonctionne pas.

20
Kobazzo

La solution ci-dessous a fonctionné pour moi. C'est une combinaison de solutions trouvées sur StackOverflow. J'ai appelé "scrollToRowAtIndexPath" après un très bref délai.

func tableViewScrollToBottom(animated: Bool) {

    let delay = 0.1 * Double(NSEC_PER_SEC)
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

    dispatch_after(time, dispatch_get_main_queue(), {

        let numberOfSections = self.tableView.numberOfSections()
        let numberOfRows = self.tableView.numberOfRowsInSection(numberOfSections-1)

        if numberOfRows > 0 {
            let indexPath = NSIndexPath(forRow: numberOfRows-1, inSection: (numberOfSections-1))
            self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: animated)
        }

    })
}

appelé par :

tableViewScrollToBottom(true)

Swift 3

func tableViewScrollToBottom(animated: Bool) {
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
        let numberOfSections = self.tableView.numberOfSections
        let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)

        if numberOfRows > 0 {
            let indexPath = IndexPath(row: numberOfRows-1, section: (numberOfSections-1))
            self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: animated)
        }
    }
}
74
Fox5150

La solution à mon problème est la suivante:

let numberOfSections = tableView.numberOfSections()
let numberOfRows = tableView.numberOfRowsInSection(numberOfSections-1)

if numberOfRows > 0 {
    println(numberOfSections)
    let indexPath = NSIndexPath(forRow: numberOfRows-1, inSection: (numberOfSections-1))
    tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: animated)
}
9
Kobazzo

Essayez de recharger avant d'appeler Scroll. Très bizarre mais fonctionne pour iOS 8. 

   tableView.reloadData()
   tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: animated)
7
Ankish Jain

Swift 3 VERSION de @ Fox5150 answer, avec une extension:

extension UITableView {

    func tableViewScrollToBottom(animated: Bool) {

        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {

            let numberOfSections = self.numberOfSections
            let numberOfRows = self.numberOfRows(inSection: numberOfSections-1)
            if numberOfRows > 0 {
                let indexPath = IndexPath(row: numberOfRows-1, section: (numberOfSections-1))
                self.scrollToRow(at: indexPath, at: UITableViewScrollPosition.bottom, animated: animated)
            }
        }
    }
}

UTILISATION:

self.tableView.tableViewScrollToBottom(animated: true)
6
Phil

J'ai le problème avec un index plus grand, disons même 15. Seuls les index jusqu'à 5-6 utilisent pour fonctionner. En faisant "animated: false", scrollToRowAtIndexPath () fonctionnait parfaitement.

2
Imran

La meilleure solution consiste à retourner la tableView afin que vous puissiez afficher le nouveau message simplement par -insertRowsAtIndexPaths:withRowAnimation:

pour le faire, vous devez d'abord retourner votre table en:

_tableView.transform = CGAffineTransformMakeScale (1,-1);

alors n'oubliez pas de retourner vos cellules, le meilleur endroit pour le faire dans -awakeFromNib en sous-classant:

- (void)awakeFromNib {
    [super awakeFromNib];
    self.contentView.transform = CGAffineTransformMakeScale (1,-1);

//if you have accessoryView
    self.accessoryView.transform = CGAffineTransformMakeScale (1,-1);
}
1
Ivan Androsenko

C'est simple. Il suffit de faire l'opération de défilement dans viewDidLayoutSubViews ()  

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.scrollToBottom(animated: false)
}

fileprivate func scrollToBottom(animated: Bool) {
    if self.items.count > 0 {
        let lastIndex = IndexPath(row: items.count - 1, section: 1)
        self.tableView.scrollToRow(at: lastIndex, at: .bottom, animated: animated)
    }
}

Il semble que tableview puisse obtenir la bonne position après les sous-vues de la présentation.

0
Lucas Lin

Si le clavier est toujours visible, vous devez définir la propriété UITableView de contentInset pour compenser la hauteur du clavier en bas de l'écran. Sinon, votre cellule pourrait être cachée derrière le clavier car la UITableView ne sait pas que la moitié de son contenu est cachée par elle.

Ou utilisez une UITableViewController qui gère cela automatiquement.

0
fluidsonic

Je sais que c'est un fil plus ancien, mais il reste pertinent. Au lieu de gérer des délais qui pourraient parfois échouer, utilisez des blocs synchrones sur le thread principal (normalement, ce n’est pas ce que je ferais, mais nous parlons en millisecondes):

DispatchQueue.main.sync {
  // Reload code goes here...
  tableView.reloadData()
}

DispatchQueue.main.sync {
  // Row, section and other checks / initialization go here...
  tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}

Cela fonctionne toujours pour moi.

0
Aaron

Heres Swift 3 version 

let numberOfSections = self.tableView.numberOfSections
let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)

if numberOfRows > 0 {
    let indexPath = NSIndexPath.init(row: numberOfRows-1, section: numberOfSections-1)
    self.tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.bottom, animated: animated)
}
0
Pankaj Gaikar

Appelez la méthode scrollToLastPosition () après le chargement de la vue table comme un appel de viewWillAppear () ou après le rechargement de la vue table lorsque quelque chose a changé en vue table

func scrollToLastPosition() {
 if !message.isEmpty {
    let indexpath = NSIndexPath(forRow: message.count - 1 , inSection:     0)
    self.tableView.scrollToRowAtIndexPath(indexpath, atScrollPosition: .Bottom, animated: true)
}
}
0
bikram sapkota
// First figure out how many sections there are
 let lastSectionIndex = self.tblTableView!.numberOfSections() - 1

// Then grab the number of rows in the last section

let lastRowIndex = 
self.tblTableView!.numberOfRowsInSection(lastSectionIndex) - 1

// Now just construct the index path

let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)

// Make the last row visible
self.tblTableView?.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)
0
Mr.Javed Multani