TableView dans une TableViewCell

Ben77650Ben77650 Membre
août 2017 modifié dans Dev. iOS, watchOS, tvOS #1

Hello,


 


Dans le cadre d'un projet au boulot je doit gérer un système assez similaire à  celui de Facebook, avec un système de commentaires, et des réponses à  un commentaire.


 


A savoir qu'aussi bien mon commentaire que ma réponse peuvent faire plusieurs lignes.


On m'a donc soufflé de faire une tableView pour les commentaires, avec à  l'intérieur de la cellule contenant le commentaire un autre tableView contenant les réponses.


 


Le souci que cela me pose est que je n'arrive pas à  faire s'afficher le texte sur le nombre de lignes nécessaire, la taille des tableView ne suivant pas.


 


Voila ci joint mon code


 


CommentViewController.m



#pragma mark - TableView delegate/datasource methods

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CommentTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@CommentTableViewCell];

CommentModel *comment = [self.dataSource itemAtIndexPath:indexPath];
cell.comment = comment;
cell.delegate = self;
cell.tableViewResponses.hidden = ![self.indexToShow containsObject:@(comment.commentId)];
cell.responseView.hidden = [self.indexToShow containsObject:@(comment.commentId)];
cell.dataSource = self.dataSource;
NSString *titleButton;

if([comment.answers count] == 0) {
cell.responseView.hidden = true;
}
else if([comment.answers count] == 1) {
titleButton = [NSString stringWithFormat:NSLocalizedString(@%ld answer, @""), [comment.answers count]];
}
else {
titleButton = [NSString stringWithFormat:NSLocalizedString(@%ld answers, @""), [comment.answers count]];
}
[cell.nbReponseButton setTitle:titleButton forState:UIControlStateNormal];

return cell;
}

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.item == [self.dataSource numberOfObjectsInSection:0] - 1 && !self.noNewObjects) {
self.page += 1;
[self fetchComments];
}
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.dataSource numberOfObjectsInSection:section];
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

UIFont *usedFont = [UIFont fontWithName:@AvenirNextCondensed-Regular size:18];

CommentModel *comment = [self.dataSource itemAtIndexPath:indexPath];
NSString *content = [comment.content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *title = [content stringByReplacingOccurrencesOfString:@ withString:@#];
CGRect textViewRect = [title
boundingRectWithSize:CGSizeMake(self.tableView.frame.size.width - 109, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:@{ NSFontAttributeName : usedFont }
context:nil];

int height = textViewRect.size.height + 40;

// if([self.indexToShow containsObject:@(comment.commentId)] && comment.answers.count != 0) {
// return [CommentTableViewCell getHeightFromTitle:comment.content andComments:comment.answers andWith:self.view.frame.size.width];
// }

if(comment.answers.count == 0) {
return height + 65;
} else {
return height + 75;
}

}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CommentModel *comment = [self.dataSource itemAtIndexPath:indexPath];
if([self.indexToShow containsObject:@(comment.commentId)]) {
[self.indexToShow removeObject:@(comment.commentId)];
[self.tableView reloadData];

if(self.messageViewHeightConstraint.constant > 0) {
[UIView animateWithDuration:0.5
animations:^{
self.messageViewHeightConstraint.constant = 0;
[self.view layoutIfNeeded];
}];
}
}
}

CommentTableViewCell.m



- (void)awakeFromNib {
[super awakeFromNib];

self.avatarImageView.layer.cornerRadius = self.avatarImageView.layer.frame.size.width / 2;
self.avatarImageView.layer.masksToBounds = YES;

UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(respondComment:)];
[self.arrowView addGestureRecognizer:singleFingerTap];

[self.tableViewResponses reloadData];
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}

-(void)setComment:(CommentModel *)comment {
_comment = comment;

NSString *content = [comment.content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

self.commentLabel.text = content;
self.usernameLabel.text = comment.user.username;

NSString *title = [content stringByReplacingOccurrencesOfString:@ withString:@#];
CGRect textViewRect = [title
boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{ NSFontAttributeName : self.commentLabel.font }
context:nil];
self.commentHeightConstraint.constant = textViewRect.size.height;
self.tableViewResponses.delegate = self;
self.tableViewResponses.dataSource = self;


self.tableViewResponses.estimatedRowHeight = 2000.0;
self.tableViewResponses.rowHeight = UITableViewAutomaticDimension;


[self.tableViewResponses reloadData];

[self.avatarImageView setImageWithURL:[NSURL URLWithString:comment.user.avatarUrl]];

[self.tableViewResponses layoutIfNeeded];
self.constraintHeightTableView.constant = self.tableViewResponses.contentSize.height + self.tableViewResponses.contentInset.bottom+self.tableViewResponses.contentInset.top;
}

+ (CGFloat)getHeightFromTitle:(NSString *)comment andComments:(NSArray<CommentModel>*)comments andWith:(CGFloat)width {
CGFloat totalHeight = 0;

UIFont *usedFont = [UIFont fontWithName:@AvenirNextCondensed-Regular size:18];

NSString *commentContent = [comment stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *title = [commentContent stringByReplacingOccurrencesOfString:@ withString:@#];
CGRect textViewRect = [title
boundingRectWithSize:CGSizeMake(width-109, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{ NSFontAttributeName : usedFont }
context:nil];

totalHeight = textViewRect.size.height+15;

for (CommentModel *com in comments) {
NSString *answerContent = [com.content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *title = [answerContent stringByReplacingOccurrencesOfString:@ withString:@#];
CGRect textViewRect = [title
boundingRectWithSize:CGSizeMake(width - 173, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{ NSFontAttributeName : usedFont }
context:nil];

int height = textViewRect.size.height + kTableViewCellAnnexesHeight + 90;

totalHeight += height;
}

return totalHeight;
}

#pragma mark - TableView delegate/datasource methods

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.comment.answers.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableViewResponses registerNib:[UINib nibWithNibName:NSStringFromClass([ResponseTableViewCell class]) bundle:nil]forCellReuseIdentifier:NSStringFromClass([ResponseTableViewCell class])];
ResponseTableViewCell *cell = [self.tableViewResponses dequeueReusableCellWithIdentifier:@ResponseTableViewCell];

CommentModel *comment = [self.comment.answers objectAtIndex:indexPath.row];
cell.comment = comment;

return cell;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

return UITableViewAutomaticDimension;
}

ResponseTableViewCell.m



- (void)awakeFromNib {
[super awakeFromNib];

self.avatarImageView.layer.cornerRadius = self.avatarImageView.layer.frame.size.width / 2;
self.avatarImageView.layer.masksToBounds = YES;
}

-(void)setComment:(CommentModel *)comment {
_comment = comment;

NSString *commentContent = [comment.content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
self.commentLabel.text = commentContent;
self.usernameLabel.text = comment.user.username;

self.commentHeightConstraint.active = false;

[self.avatarImageView setImageWithURL:[NSURL URLWithString:comment.user.avatarUrl]];
}

PS: Je sais qu'un tableView dans une cell est pas recommandé mais j'ai pas eu d'autres choix.


 


Merci à  ceux qui m'aideront :)


Réponses

  • Joanna CarterJoanna Carter Membre, Modérateur

    De mon avis, il ne faut pas utiliser une deuxième tableView.


     


    Vaut mieux d'avoir (au moins) deux type de cellules dans une tableView. En fait, ça pourrait être difficile, voire presque impossible.


     


    N'oublies pas qu'il faut considérer comment gérer le développement et l'écroulement des conversations.


  • J'étais sur que quelqu'un dirait ça.


    On a pas vraiment le choix, et une 2e cellule, ça ne rends pas comme le client aimerait...


    Et de toute facebook utilise bien des tableaux dans ses cellules de tableau non ?


  • Joanna CarterJoanna Carter Membre, Modérateur


    J'étais sur que quelqu'un dirait ça.




     


    Donc tu n'es pas déçu  >:D


     




    On a pas vraiment le choix, et une 2e cellule, ça ne rends pas comme le client aimerait...




     


     


    On a toujours le choix, même s'il faut dessiner par main sur une UIScrollView  :-*


     




    Et de toute facebook utilise bien des tableaux dans ses cellules de tableau non ?




     


     


    Bah non ! Je viens de voir sur mon portable et là  je vois que, apparement, il utilise une tableView pour le premier "niveau" de la conversation puis il faut cliquer sur (n) commentaires pour voir la reste des commentaires ; ce qui affiche un deuxième viewController qui contient une tableView, dédié à  afficher les commentaires, à  part du premier viewController.


     


    En aucun cas, je ne vois pas une tableView dans une cellule

  • Ce que tu viens de décrire de Facebook c'est exactement ce que j'ai.


    Et moi je vois un commentaire dans une cellule d'un 1er tableView, et dans cette cellule un bouton, et un 2e tableView.


     


    Et vu les deadlines non je n'avais pas le choix et le lead m'a aiguillé la dessus...


  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #6


    Et moi je vois un commentaire dans une cellule d'un 1er tableView, et dans cette cellule un bouton, et un 2e tableView.




     


    Encore bah non ! ça pourrait être une UIStackView, ce qui est tout autre chose qu'une UITableView. N'importe comment les "segments" sont fait, il n'y a qu'un segment avec un commentaire et le bouton pour afficher le reste dans une tableView à  part.


     


    Et, avec la UIStackView avec un seul segment pour le premier commentaire, on n'aura pas les mêmes difficultés qu'avec une UITableView avec l'intégralité de la conversation.


     




    Et vu les deadlines non je n'avais pas le choix et le lead m'a aiguillé la dessus...




     


     


    Tu dis que l'on n'as pas discuté comment le faire avant d'arriver à  un deadline imminent ?


     


    Et, en plus, ton lead fait les suppositions en ne savant pas vraiment ce que Facebook a fait. Il y a plusieurs alternatives que l'on puisse choisir.


  • Je dis juste qu'on nous as dit il faut une v2 ça doit intégrer ça entre autres, et ça doit être fini pour la fin de semaine voila ^^


  • Joanna CarterJoanna Carter Membre, Modérateur

    Bien. à‰videmment, tu as déjà  décidé comment le faire ; pourquoi tu es venu ici pour demander ?


     


    Ou, peut-être, c'est parce que tu ne peux pas arriver à  le faire ? Et il ne reste qu'un jour pour trouver la solution ?


     


    Si ton lead est convaincu que la deuxième tableView est possible avant demain, il faut lui dire de trouver comment le faire lui-même.


     


    Sinon, il faut considérer les autres solutions. Tu n'acceptes pas l'idée d'une UIStackView ; peut-être tu devrais considérer, à  la place d'une cellule par conversation, d'utiliser une section pour chaque conversation avec une cellule pour chaque commentaire là  dedans.

  • Loi 1 - Le patron a toujours raison


    Loi 2 - Si le patron a tort, se référer à  la loi 1



  • Bien. à‰videmment, tu as déjà  décidé comment le faire ; pourquoi tu es venu ici pour demander ?


     


    Ou, peut-être, c'est parce que tu ne peux pas arriver à  le faire ? Et il ne reste qu'un jour pour trouver la solution ?


     


    Si ton lead est convaincu que la deuxième tableView est possible avant demain, il faut lui dire de trouver comment le faire lui-même.


     


    Sinon, il faut considérer les autres solutions. Tu n'acceptes pas l'idée d'une UIStackView ; peut-être tu devrais considérer, à  la place d'une cellule par conversation, d'utiliser une section pour chaque conversation avec une cellule pour chaque commentaire là  dedans.




     


    Le lead est convaincu de cela, sauf qu'il y a 2 ou 3 projets à  terminer pour demain (fermeture de l'entreprise pour vacances, donc de grosses deadlines).


     


    Et oui ma deadline est pour demain, et je me sens mal de tout refaire et tout finir d'ici demain soir, pour ça que j'essaye de faire marcher ma solution actuelle (qui n'est pas la plus pertinente j'en conviens).

  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #11

    Occupes-toi sur la solution d'utiliser un section pour chaque conversation, avec une cellule pour chaque commentaire.


     


    ça c'est faisable assez vite et c'est garantie de marcher  8--)


     


    https://stackoverflow.com/questions/27902601/uitableview-cell-dyanmic-height-with-uitableview-inside-the-uitableview-cell


  • Mais avec ce système ça aura pas un look a la Facebook, si ? ^^


  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #13

    Mais si !


     


    Regardes les lignes entre les conversations ; elles sont assez épaisses et grises. Ceux sont les "headers" des sections.


  • Oki je regarderais ça demain alors, merci ;)


  • Joanna CarterJoanna Carter Membre, Modérateur

    Oh, et, après les vacances, tu devrais changer au moins une partie de ton code en remplaçant les multiples if...else avec un switch :



    switch comment.answers.count
    {
    case 0:

    cell.responseView.hidden = true;

    break;

    case 1:

    titleButton = [NSString stringWithFormat:NSLocalizedString(@%ld answer, @""), [comment.answers count]];
    break;

    default:

    titleButton = [NSString stringWithFormat:NSLocalizedString(@%ld answers, @""), [comment.answers count]];
    break;
    }

    ::)  8--) 


  • Après les vacances le projet sera surement livré donc bon ^^


     


    Et puis bon c'est pas comme si celui qui avais fait la V1 m'avais laissé un projet dégueulasse x)


  • Joanna CarterJoanna Carter Membre, Modérateur
    août 2017 modifié #17

    Encore un projet livré avec le mauvais code qui cache les bogues en attendant les retours négatifs des utilisateurs.


     


    Et, sans doute, on ne l'a pas testé rigoureusement car on n'a pas eu le temps  >:(


     


    Dis-moi, c'est quelle entreprise ; afin que je puisse éviter d'acheter leurs produits ou, pire, de travailler là   :-*


     


    Quand-même, pour toi, sur la localisation des quantités comme "1 answer", "n answers", lis cet article pendant les vacances  :-*


     


    http://crunchybagel.com/localizing-plurals-in-ios-development/


  • Si tu voyais l'état de la V1 c'est à  en pleurer.


    Et si ça a été testé quand même ;)


     


    Et concernant la société je me garde le nom, juste c'est une agence mobile, qui développe des projets pour un de ses clients, et je ne crois pas une seule seconde que le produit que je développe va marcher (trop de concurrence) et concernant ma société actuelle c'est mon dernier jour chez eux demain (gestion de projet chaotique, j'ai décidé de partir).


    Je regarderais malgré tout ton article merci à  toi :)


  • Joanna CarterJoanna Carter Membre, Modérateur

    Dans ce cas là , bonnes vacances et bonne continuation pour le prochain contrat 


Connectez-vous ou Inscrivez-vous pour répondre.