121 votes

UITableViewCell avec UITextView hauteur en iOS 7 ?

Comment puis-je calculer la hauteur d'un UITableViewCell avec un UITextView dans iOS 7?

J'ai trouvé beaucoup de réponses sur des questions similaires, mais sizeWithFont: participe à toutes les solutions et cette méthode est déconseillée!

Je sais que je dois utiliser - (CGFloat)tableView:heightForRowAtIndexPath: mais comment puis-je calculer la hauteur dont mon TextView a besoin pour afficher le texte entier?

428voto

Tim Bodeit Points 3644

Tout d'abord, il est très important de noter, qu'il y a une grande différence entre UITextView et UILabel quand il s'agit de la façon dont le texte est rendu. Non seulement UITextView ont encarts sur toutes les frontières, mais aussi la mise en forme de texte à l'intérieur, il est légèrement différent.

Par conséquent, sizeWithFont: est une mauvaise manière de faire pour UITextViews.

Au lieu de cela UITextView lui-même a une fonction appelée sizeThatFits: qui sera de retour le plus petit de la taille nécessaire pour afficher tout le contenu de l' UITextView à l'intérieur d'une zone de délimitation, que vous pouvez spécifier.

Ce qui suit fonctionnera aussi pour les deux iOS 7 et les versions plus anciennes et de plein droit maintenant, ne pas inclure toutes les méthodes, qui sont désormais obsolètes.


La Solution La Plus Simple

- (CGFloat)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width {
    UITextView *calculationView = [[UITextView alloc] init];
    [calculationView setAttributedText:text];
    CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
    return size.height;
}

Cette fonction prend un NSAttributedString et la largeur souhaitée en tant que CGFloat et le retour de la hauteur nécessaire


Une Solution Détaillée

Depuis que j'ai récemment fait quelque chose de similaire, j'ai pensé que je voudrais partager des solutions aux Questions connexes que j'ai rencontré. J'espère que ça aidera quelqu'un.

C'est beaucoup plus en profondeur et portera sur les éléments suivants:

  • Bien sûr: le réglage de la hauteur d'un UITableViewCell basé sur la taille nécessaire pour afficher le contenu complet d'un contenue UITextView
  • Répondre à des modifications du texte (et d'animer la hauteur de la ligne)
  • En gardant le curseur à l'intérieur de la zone visible et de maintien de premiers répondants sur l' UITextView lors du redimensionnement de la UITableViewCell lors de l'édition

Si vous travaillez avec un tableau statique de la vue ou vous avez seulement un nombre connu d' UITextViews, vous pouvez potentiellement faire de l'étape 2 est beaucoup plus simple.

1. Tout d'abord, remplacer le heightForRowAtIndexPath:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // check here, if it is one of the cells, that needs to be resized
    // to the size of the contained UITextView
    if (  )             
        return [self textViewHeightForRowAtIndexPath:indexPath];
    else
    // return your normal height here:
            return 100.0;           
}

2. Définir la fonction qui calcule le besoin de la hauteur:

Ajouter un NSDictionary (dans cet exemple appelle textViews) comme une variable d'instance de votre UITableViewController sous-classe.

L'utilisation de ce dictionnaire pour stocker les références à la personne UITextViews comme:

(et oui, indexPaths sont les clés valides pour les dictionnaires)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Do you cell configuring ...

    [textViews setObject:cell.textView forKey:indexPath];
    [cell.textView setDelegate: self]; // Needed for step 3

    return cell;
}

Cette fonction va maintenant calculer la hauteur réelle:

- (CGFloat)textViewHeightForRowAtIndexPath: (NSIndexPath*)indexPath {
    UITextView *calculationView = [textViews objectForKey: indexPath];
    CGFloat textViewWidth = calculationView.frame.size.width;
    if (!calculationView.attributedText) {
        // This will be needed on load, when the text view is not inited yet

        calculationView = [[UITextView alloc] init];
        calculationView.attributedText = // get the text from your datasource add attributes and insert here
        textViewWidth = 290.0; // Insert the width of your UITextViews or include calculations to set it accordingly
    }
    CGSize size = [calculationView sizeThatFits:CGSizeMake(textViewWidth, FLT_MAX)];
    return size.height;
}

3. Activer le Redimensionnement lors de l'Édition

Pour les deux fonctions, il est important, que le délégué de l' UITextViews est mis à votre UITableViewController. Si vous avez besoin de quelque chose d'autre en tant que délégué, vous pouvez contourner le problème en faisant l'appel, ou bien, à l'aide de la NSNotificationCenter crochets.

- (void)textViewDidChange:(UITextView *)textView {

    [self.tableView beginUpdates]; // This will cause an animated update of
    [self.tableView endUpdates];   // the height of your UITableViewCell

    // If the UITextView is not automatically resized (e.g. through autolayout 
    // constraints), resize it here

    [self scrollToCursorForTextView:textView]; // OPTIONAL: Follow cursor
}

4. Suivez curseur lors de l'Édition

- (void)textViewDidBeginEditing:(UITextView *)textView {
    [self scrollToCursorForTextView:textView];
}

Cela va rendre l' UITableView faites défiler jusqu'à la position du curseur, si elle n'est pas dans le visible Rect de la UITableView:

- (void)scrollToCursorForTextView: (UITextView*)textView {

    CGRect cursorRect = [textView caretRectForPosition:textView.selectedTextRange.start];

    cursorRect = [self.tableView convertRect:cursorRect fromView:textView];

    if (![self rectVisible:cursorRect]) {
        cursorRect.size.height += 8; // To add some space underneath the cursor
        [self.tableView scrollRectToVisible:cursorRect animated:YES];
    }
}

5. Ajuster visible rect, en définissant les encarts

Pendant le montage, les pièces de votre UITableView peuvent être couverts par le Clavier. Si le tableviews encarts ne sont pas ajustés, scrollToCursorForTextView: ne sera pas en mesure de faire défiler le curseur, si il est en bas de la tableview.

- (void)keyboardWillShow:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, kbSize.height, 0.0);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardWillHide:(NSNotification*)aNotification {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.35];
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, 0.0, 0.0);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
    [UIView commitAnimations];
}

Et la dernière partie:

À l'intérieur de votre point de vue ne charge, inscrivez-vous pour les Notifications pour Clavier changements par le biais d' NSNotificationCenter:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

Veuillez ne pas obtenir en colère contre moi, pour la fabrication de cette réponse si longtemps. Bien que pas tous, il est nécessaire de répondre à la question, je crois qu'il y a d'autres personnes qui directement liés à des questions leur seront utiles.


Mise à JOUR:

Comme Dave Haupert souligné, j'ai oublié d'inclure l' rectVisible fonction de:

- (BOOL)rectVisible: (CGRect)rect {
    CGRect visibleRect;
    visibleRect.origin = self.tableView.contentOffset;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size = self.tableView.bounds.size;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;

    return CGRectContainsRect(visibleRect, rect);
}

Aussi, j'ai remarqué, que scrollToCursorForTextView: toujours inclus une référence directe à l'un des objets textfield dans mon projet. Si vous avez un problème avec bodyTextView de ne pas être trouvé, vérifiez la version mise à jour de la fonction.

37voto

manecosta Points 1210

Il existe une nouvelle fonction pour remplacer sizeWithFont, qui est boundingRectWithSize.

J'ai ajouté la fonction suivante à mon projet, qui utilise la nouvelle fonction sur iOS7 et l'ancienne sur iOS inférieure à 7. Elle a fondamentalement la même syntaxe que sizeWithFont:

     -(CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{
        if(IOS_NEWER_OR_EQUAL_TO_7){
            NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                              font, NSFontAttributeName,
                                              nil];

            CGRect frame = [text boundingRectWithSize:size
                                              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                           attributes:attributesDictionary
                                              context:nil];

            return frame.size;
        }else{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
            return [text sizeWithFont:font constrainedToSize:size];
#pragma clang diagnostic pop
        }
    }
 

Vous pouvez ajouter cela IOS_NEWER_OR_EQUAL_TO_7 sur votre fichier prefix.pch dans votre projet en tant que:

 #define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )
 

5voto

PowerQian Points 166

Tim Bodeit la réponse est grande. J'ai utilisé le code de la Solution la plus Simple pour correctement obtenir la hauteur de l'affichage de texte, et de profiter de la hauteur en heightForRowAtIndexPath. Mais je n'utilise pas le reste de la réponse à redimensionner l'affichage de texte. Au lieu de cela, j'écris le code pour modifier l' frame de l'affichage du texte en cellForRowAtIndexPath.

Tout fonctionne sous iOS 6 et au-dessous, mais dans iOS 7, le texte ne peut pas être entièrement affichée, même si l' frame de l'affichage du texte est en effet de taille. (Je ne suis pas en utilisant Auto Layout). Elle doit être la raison pour laquelle dans iOS 7 il y a TextKit et la position du texte est contrôlée par NSTextContainer en UITextView. Donc dans mon cas j'ai besoin d'ajouter une ligne pour définir le someTextView afin de le faire fonctionner correctement dans iOS 7.

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        someTextView.textContainer.heightTracksTextView = YES;
    }

La documentation dit, que cette propriété n'est:

Contrôle si le récepteur ajuste la hauteur de son cadre rectangle lorsque son mode texte est redimensionné. Valeur par défaut: NON.

Si le laisser à la valeur par défaut, après redimensionner l' frame de someTextView, la taille de l' textContainer n'est pas changé, entraînant le résultat que le texte ne peut être affichée dans la zone avant de la redimensionner.

Et peut-être il est nécessaire de définir l' scrollEnabled = NO dans le cas où il n'y a plus d'un textContainer, alors que le texte de redistribution à partir d'un textContainer pour l'autre.

4voto

Zorayr Points 2637

Voici encore une solution qui vise à la simplicité et le prototypage rapide:

Programme d'installation:

  1. Table avec des prototypes de cellules.
  2. Chaque cellule contient dynamique de la taille d' UITextView w/ autres matières.
  3. Prototype de cellules sont associées avec TableCell.h.
  4. UITableView est associée TableViewController.h.

Solution:

(1) Ajouter à l' TableViewController.m:

 // This is the method that determines the height of each cell.  
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    // I am using a helper method here to get the text at a given cell.
    NSString *text = [self getTextAtIndex:indexPath];

    // Getting the height needed by the dynamic text view.
    CGSize size = [self frameForText:text sizeWithFont:nil constrainedToSize:CGSizeMake(300.f, CGFLOAT_MAX)];

    // Return the size of the current row.
    // 80 is the minimum height! Update accordingly - or else, cells are going to be too thin.
    return size.height + 80; 
}

// Think of this as some utility function that given text, calculates how much 
// space would be needed to fit that text.
-(CGSize)frameForText:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          font, NSFontAttributeName,
                                          nil];
    CGRect frame = [text boundingRectWithSize:size
                                      options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                   attributes:attributesDictionary
                                      context:nil];

    // This contains both height and width, but we really care about height.
    return frame.size;
}

// Think of this as a source for the text to be rendered in the text view. 
// I used a dictionary to map indexPath to some dynamically fetched text.
- (NSString*) getTextAtIndex: (NSIndexPath *) indexPath
{
    return @"This is stubbed text - update it to return the text of the text view.";
}

(2) Ajouter à la TableCell.m:

// This method will be called when the cell is initialized from the storyboard
// prototype. 
- (void)awakeFromNib
{
    // Assuming TextView here is the text view in the cell. 
    TextView.scrollEnabled = YES;
}

Explication:

Donc ce qui se passe ici est que chaque mode texte est lié à la hauteur de la table de cellules verticales et horizontales des contraintes que cela signifie que lorsque la table de la hauteur de la cellule augmente, l'affichage de texte augmente sa taille. J'ai utilisé une version modifiée de @manecosta code pour calculer la hauteur d'un texte en vue d'adapter le texte donné dans une cellule. Ce qui signifie que la donnée d'un texte avec un nombre X de caractères, frameForText: sera de retour d'une taille qui ont une propriété size.height qui correspond à l'affichage du texte de la hauteur requise.

Maintenant, tout ce qui reste est la mise à jour de la cellule hauteur pour correspondre le texte d'une vue en hauteur. Et ceci est réalisé au heightForRowAtIndexPath:. Comme indiqué dans les commentaires, depuis size.height seulement de la hauteur de l'affichage de texte et de ne pas l'intégralité de la cellule, il doit y avoir un décalage ajouté. Dans le cas de l'exemple, cette valeur était de 80.

3voto

TomSwift Points 22012

Une approche si vous utilisez la mise en page automatique est de laisser la mise en page automatique du moteur de calculer la taille pour vous. Ce n'est pas l'approche la plus efficace, mais il est très pratique (et sans doute le plus précis). Il devient plus pratique que de la complexité de la cellule de mise en page pousse - par exemple, soudain, vous avez deux ou plus de deux textviews/champs dans la cellule.

J'ai répondu à une question similaire, avec un exemple complet pour le dimensionnement de la tableview cellules à l'aide de mise en page automatique, ici:

Comment redimensionner superview pour s'adapter à tous les sous-vues avec mise en page automatique?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X