2010-01-28 4 views
23

Dans l'application Messages d'Apple, lorsque vous cliquez sur le nom d'un correspondant et que vous passez à la vue table de la conversation (avec des bulles pour chaque message), la table apparaît jusqu'à la fin. Aucune animation ou quoi que ce soit, c'est juste là.Comment démarrer UITableView sur la dernière cellule?

De même, dans Tweetie 2, lorsque vous chargez l'affichage des tweets, il apparaît exactement là où vous l'avez regardé pour la dernière fois. Aucune animation pour y arriver, c'est juste là, comme si aucune des cellules ci-dessus n'était chargée.

Comment ces applis se comportent-elles? Appellent-ils scrollToRowAtIndexPath:atScrollPosition:animated: quelque part dans le contrôleur de table? Si oui, comment savent-ils ce qu'il faut passer à atScrollPosition:? Et dans quelle méthode est-il appelé?

Répondre

54

scrollToRowAtIndexPath devrait fonctionner.

En viewWillAppear:, essayez ceci:

[theTableView reloadData];  
NSIndexPath* ip = [NSIndexPath indexPathForRow:rowNumberHere inSection:sectionNumberHere]; 
[theTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO]; 

rowNumberHere est le numéro de ligne de la source de données que vous souhaitez défiler.

atScrollPosition est juste une des valeurs dans l'énumération UITableViewScrollPosition qui peut déterminer où à l'écran le numéro de ligne que vous souhaitez afficher. Cependant, en fonction du nombre de lignes et de la ligne sur laquelle vous faites défiler, cela peut ne pas faire la différence.

La mise à reloadData: permet d'éviter une exception si les données ne sont pas encore chargées dans viewWillAppear:. Si vous mettez le scrollToRowAtIndexPath dans viewDidAppear:, vous n'auriez pas besoin du reloadData: mais vous verrez la table sauter un peu que vous dites ne pas vouloir.

Edit: @Theory, essayez de changer votre code comme suit ...

[tableView reloadData]; 
int lastRowNumber = [tableView numberOfRowsInSection:0] - 1; 
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0]; 
[tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO]; 

S'il vous plaît noter numberOfRowsInSection retours nombre de lignes, pas le dernier numéro de ligne (ce qui est le nombre de lignes - 1).

+0

Merci, cela semble être exactement ce que je devais savoir, sauf que ma mise en œuvre bloque l'application. :-(Voir ma "réponse" ci-dessous – theory

+0

D'oh bien sûr, j'ai manqué que je voulais le numéro d'index de la cellule pas le numéro de la ligne.Merci! Je vais mettre à jour ma propre réponse pour montrer ma solution spécifique – theory

+0

Votre note à propos du nombre de lignes et de la position des lignes, cela peut ne pas faire la différence, merci de nous aider à résoudre ce problème – carbonr

9

Vous pouvez appeler -scrollToRowAtIndexPath: atScrollPosition: animé dans la méthode -viewWillAppear: de votre TableViewController. AtScrollPosition: vous permet de définir où vous voulez que votre cellule pour rowAtIndexPath apparaisse. Il y a quatre options:

UITableViewScrollPositionTop - met votre cellule de droite en haut de la vue

UITableViewScrollPositionMiddle - centre votre cellule dans la vue

UITableViewScrollPositionBottom - met votre cellule en bas

UITableViewScrollPositionNone - L'utilisation de ce paramètre permet de positionner la cellule dans la vue utilisateur avec un défilement/déplacement minimal.

Le comportement est différent dans trois scénarios: -

Si la cellule est déjà en vue, il ne fait rien.

Si la cellule est au-dessus de la vue actuelle, elle fait défiler la cellule vers le haut de la vue.

Si la cellule est en dessous de la vue actuelle, elle fait défiler la cellule vers le bas de la vue.

7

Après la réponse de DyingCactus ci-dessus, j'ai ajouté cette méthode pour mon contrôleur:

-(void)viewWillAppear:(BOOL)animated { 
     [self.tableView reloadData];  
     NSIndexPath* ip = [NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:0] - 1 inSection:0]; 
     [self.tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO]; 
} 

Et maintenant, il fonctionne, exactement ce que je voulais. Merci!

+0

La ligne sur laquelle vous définissez NSIndexPath ne semble pas correcte. Je vais éditer ma réponse avec ce que je pense qu'elle devrait être. – DyingCactus

+0

Modifié pour refléter le correctif. Idiot de ne pas avoir réalisé que je passais 'mumberOfRowsInSection' plutôt que l'index. Merci encore! – theory

+2

Génial. Vous pouvez également ajouter une vérification pour zéro rangées et ne pas appeler le parchemin dans ce cas à moins que votre vue de table ait toujours au moins une ligne. – DyingCactus

0

Note pour faire défiler la rangée du bas, la section doit être la dernière section de 0 (première section):

int lastSection = [self.myTableView numberOfSections] -1; 
if (lastSection < 0) return; 

int lastRow = [self.myTableView numberOfRowsInSection:lastSection] - 1; 
if (lastRow < 0) return; 
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRow inSection:lastSection]; 

[self.myTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
+0

func scrollToBot() { let lastSection = self.numberOfSections - 1 garde lastSection> = 0 else {return} laisser LastRow = self.numberOfRowsInSection (lastSection) - 1 garde LastRow> = 0 else {return} let indexPath = NSIndexPath (pourRow: lastRow, inSection: lastSection) self.scrollToRowAtIndexPath (indexPath, atScrollPosition: .Top, animé: false) } – Aleksandr

7

J'utilise autolayout et aucune des réponses 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)]; 
    } 
} 
+1

Cela a provoqué le défilement excessif de ma vue de table, donc j'ai utilisé '[self.tableView setContentOffset : CGPointMake (0, self.tableView.contentSize.height - self.tableView.frame.size.height)]; ', et pour l'utiliser après que la vue a été chargée, vous devez vous assurer que toutes les cellules sont dessinées, comme' [[ self performSelector: withObject: afterDelay: ' – user114111121

+0

il travaille pour moi! @ user114111121 – noodles

+0

A travaillé pour moi en remplaçant CGFLOAT_MAX par la hauteur calculée. J'ai également éliminé le saut visuel ennuyeux sans animations qui était visible avec toutes les autres solutions que j'avais essayées. –

0
#import "ViewController.h" 


@interface ViewController() 
@end 

@implementation ViewController 
CGFloat labelWidth = 260.0f; 
CGFloat labelRequiredHeight = 180.0f; 
@synthesize tblView; 
@synthesize txtField; 
@synthesize chatData; 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    tblView.delegate = self; 

    [self.tblView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; 
    chatData = [[NSMutableArray alloc] init]; 
    [self registerForKeyboardNotifications]; 

} 

-(IBAction) textFieldDoneEditing : (id) sender 
{ 
    NSLog(@"the text content%@",txtField.text); 
    [sender resignFirstResponder]; 
    [txtField resignFirstResponder]; 
} 

- (IBAction)sendButton:(id)sender 
{ 
    if (txtField.text.length>0) { 
     // updating the table immediately 
     NSArray *data = [NSArray arrayWithObject:@"text"]; 
     NSArray *objects = [NSArray arrayWithObject:txtField.text]; 
     NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects forKeys:data]; 
     [chatData addObject:dictionary]; 

     NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init]; 
     NSIndexPath *newPath = [NSIndexPath indexPathForRow:0 inSection:0]; 
     [insertIndexPaths addObject:newPath]; 
     [tblView beginUpdates]; 
     [tblView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop]; 
     [tblView endUpdates]; 
     [tblView reloadData]; 

     txtField.text = @""; 
     [self.view endEditing:YES]; 
    } 
} 

-(IBAction) backgroundTap:(id) sender 
{ 
    [self.txtField resignFirstResponder]; 
} 

-(BOOL)SendbtnShouldReturn:(UITextField *)textfield 
{ 
    [textfield resignFirstResponder]; 
    return YES; 
} 

- (BOOL)textFieldShouldReturn:(UITextField *)textField 
{ 
    NSLog(@"the text content%@",txtField.text); 
    [textField resignFirstResponder]; 
    if (txtField.text.length>0) 
    { 
     // updating the table immediately 
     NSArray *keys = [NSArray arrayWithObject:@"text"]; 
     NSArray *objects = [NSArray arrayWithObject:txtField.text]; 
     NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys]; 
     [chatData addObject:dictionary]; 

     NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init]; 
     NSIndexPath *newPath = [NSIndexPath indexPathForRow:0 inSection:0]; 
     [insertIndexPaths addObject:newPath]; 
     [tblView beginUpdates]; 
     [tblView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop]; 
     [tblView endUpdates]; 
     [tblView reloadData]; 
     txtField.text = @""; 
    } 
    return NO; 
} 


// Keyboard Functionality 

-(void) registerForKeyboardNotifications 
{ 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 
} 

-(void) freeKeyboardNotifications 
{ 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 
} 

-(void) keyboardWasShown:(NSNotification*)aNotification 
{ 
    NSLog(@"Keyboard was shown"); 
    NSDictionary* info = [aNotification userInfo]; 
    // Get animation info from userInfo 
    NSTimeInterval animationDuration; 
    UIViewAnimationCurve animationCurve; 
    CGRect keyboardFrame; 
    [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; 
    [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; 
    [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardFrame]; 
    // Move 
    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:animationDuration]; 
    [UIView setAnimationCurve:animationCurve]; 
    NSLog(@"frame..%f..%f..%f..%f",self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height); 
    NSLog(@"keyboard..%f..%f..%f..%f",keyboardFrame.origin.x, keyboardFrame.origin.y, keyboardFrame.size.width, keyboardFrame.size.height); 
    [self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y- keyboardFrame.size.height, self.view.frame.size.width, self.view.frame.size.height)]; 
    [tblView setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+ keyboardFrame.size.height, self.view.frame.size.width, self.view.frame.size.height-keyboardFrame.size.height)]; 
    [tblView scrollsToTop]; 
    [UIView commitAnimations]; 

} 

-(void) keyboardWillHide:(NSNotification*)aNotification 
{ 
    NSLog(@"Keyboard will hide"); 
    NSDictionary* info = [aNotification userInfo]; 
    // Get animation info from userInfo 
    NSTimeInterval animationDuration; 
    UIViewAnimationCurve animationCurve; 
    CGRect keyboardFrame; 
    [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; 
    [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; 
    [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardFrame]; 
    // Move 
    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:animationDuration]; 
    [UIView setAnimationCurve:animationCurve]; 
    [self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y + keyboardFrame.size.height, self.view.frame.size.width, self.view.frame.size.height)]; 
    [tblView setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)]; 
    [UIView commitAnimations]; 
    UIEdgeInsets contentInsets = UIEdgeInsetsZero; 
    self.tblView.contentInset = contentInsets; 
    self.tblView.scrollIndicatorInsets = contentInsets; 
    self.tblView.scrollEnabled=chatData; 


} 

#pragma mark UITableViewDataSource protocol methods 
- (void)scrollTableToBottom 
{ 
    int rowNumber = [self.tblView numberOfRowsInSection:1]; 
    if (rowNumber > 0) [self.tblView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rowNumber-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES]; 
} 


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [chatData count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *[email protected]"chatCell"; 
    chatCell *cell = (chatCell *)[tableView dequeueReusableCellWithIdentifier: @"chatCellIdentifier"]; 
    if(!cell) 
     cell =[[chatCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 
// NSUInteger row = [chatData count]-[indexPath row]-1; 
    NSUInteger row=[indexPath row]; 
    NSUInteger count = [chatData count]; 
    if (row <chatData.count) 
    { 
     NSString *chatText = [[chatData objectAtIndex:row] objectForKey:@"text"]; 
     cell.txtMsg.text = chatText; 
    } 
    return cell; 
} 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString *cellText = [[chatData objectAtIndex:chatData.count-indexPath.row-1] objectForKey:@"text"]; 
    UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:20.0]; 
    CGSize constraintSize = CGSizeMake(225.0f, MAXFLOAT); 
    CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap]; 
     return labelSize.height + 40; 
} 


//-(void)scrollToBottomTableView 
//{ 
// if (self.tblView.contentOffset.y > self.tblView.frame.size.height) 
// { 
//  [self.tblView scrollToRowAtIndexPath:[self. indexPathForLastMessage] 
//        atScrollPosition:UITableViewScrollPositionBottom animated:YES]; 
// } 
//} 


-(void)viewWillAppear:(BOOL)animated 
{ 

//  [tblView reloadData]; 
// 
// int lastRowNumber = [tblView numberOfRowsInSection:0] - 1; 
// NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0]; 
// [tblView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO]; 
} 
-(void)viewDidAppear:(BOOL)animated 
{ 
//[tblView reloadData]; 

} 
- (void)reloadTableViewDataSource 
{ 
    [tblView reloadData]; 

} 

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 
@end 
+0

mon code ne pas ajouter de texte de la dernière cellule de la table .... comme l'application de chat ... plz aidez-moi – jeetendra

+0

Vous devez également décrire ce que fait votre code ou pourquoi. –

+0

mon code ajoute le texte du haut de la table et ne défile pas à la dernière rangée de la table. – jeetendra

3

Le problème avec la méthode scrollToRowAtIndexPath est son lent et le tableView prend du temps pour faire défiler vers le bas.

J'ai eu exactement le même problème, après avoir essayé tout (comme vous), cela a fonctionné, la clé est de savoir si vous utilisez autolayout initialiser scrollToBottom true puis faire ce

- (void)viewDidLayoutSubviews { 
    [super viewDidLayoutSubviews]; 
    // Scroll table view to the last row 
    [self scrollToBottom]; 
} 

-(void)scrollToBottom { 
    if (shouldScrollToLastRow) 
    { 
     CGPoint bottomOffset = CGPointMake(0, self.tableView.contentSize.height - self.tableView.bounds.size.height); 
     [self.tableView setContentOffset:bottomOffset animated:NO]; 
    } } 

faisant s'assurera que vous êtes presque au fond de tableView mais peut-être pas tout à fait en bas car il est impossible de connaître le décalage inférieur exact lorsque vous êtes en haut de la tableView, donc après cela, nous pouvons implémenter scrollViewDidScroll

-(void)scrollViewDidScroll: (UIScrollView*)scrollView 
{ 
    float scrollViewHeight = scrollView.frame.size.height; 
    float scrollContentSizeHeight = scrollView.contentSize.height; 
    float scrollOffset = scrollView.contentOffset.y; 

    // if you're not at bottom then scroll to bottom 
    if (!(scrollOffset + scrollViewHeight == scrollContentSizeHeight)) 
    { 
     [self scrollToBottom]; 
    } else { 
    // bottom reached now stop scrolling 
     shouldScrollToLastRow = false; 
    } 
} 
5

Réponse de @DyingCactus à Swift 3:

let lastRow: Int = self.tableView.numberOfRows(inSection: 0) - 1 
    let indexPath = IndexPath(row: lastRow, section: 0); 
    self.tableView.scrollToRow(at: indexPath, at: .top, animated: false) 
Questions connexes