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.
Je crois que l'appel de [table setContentOffset:CGPointMake(0, CGFLOAT_MAX)]
fera ce que vous voulez.
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];
}
}
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)
}
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];
}
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)];
}
}
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.
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)
}
}
}
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)
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.
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.
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)
}
Vous devriez utiliser UITableViewScrollPositionBottom
à la place.
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)
}
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)
})
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.)
Dans Swift 3.0
self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), animated: true)
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)];
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 );
}
}
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
}
}
}
}
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)
}
}
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.
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];
}
Si vous configurez un cadre pour tableview par programme, assurez-vous de le définir correctement.
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
Pas besoin de faire défiler, vous pouvez simplement le faire en utilisant ce code:
[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
[self.tableViewInfo scrollRectToVisible: CGRectMake (0, self.tableViewInfo.contentSize.height-self.tableViewInfo.height, self.tableViewInfo.width, self.tableViewInfo.height) animé: YES];
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];
}
}
Dans Swift, vous avez juste besoin
self.tableView.scrollToNearestSelectedRowAtScrollPosition(UITableViewScrollPosition.Bottom, animated: true)
pour le faire défiler automatiquement vers le bas
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)
}
}
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)
}
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)
}
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)
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)
}
}