web-dev-qa-db-fra.com

UITableViewCell avec la hauteur UITextView dans 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: prend part à 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 tout le texte?

121
MyJBMe

Tout d'abord, il est très important de noter qu'il existe une grande différence entre UITextView et UILabel en ce qui concerne le rendu du texte. UITextView comporte non seulement des encarts sur toutes les bordures, mais la disposition du texte est légèrement différente.

Par conséquent, sizeWithFont: est un mauvais moyen d’utiliser UITextViews.

Au lieu de cela, UITextView a lui-même une fonction appelée sizeThatFits: qui renvoie la plus petite taille nécessaire pour afficher tout le contenu de la UITextView à l'intérieur d'un cadre de sélection que vous pouvez spécifier.

Ce qui suit fonctionnera de la même manière pour iOS 7 et les versions plus anciennes et, à l'heure actuelle, n'inclut aucune méthode, qui est obsolète.


Solution 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 sous la forme CGFloat et renvoie la hauteur nécessaire


Solution détaillée

Depuis que j'ai récemment fait quelque chose de similaire, je pensais partager quelques solutions aux problèmes que je rencontrais. J'espère que cela aidera quelqu'un.

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

  • Bien sûr: régler la hauteur d'un UITableViewCell en fonction de la taille requise pour afficher tout le contenu d'un contenu UITextView
  • Répondre aux modifications de texte (et animer les modifications de hauteur de la ligne)
  • Garder le curseur dans la zone visible et laisser le premier intervenant sur la UITextView lors du redimensionnement de la UITableViewCell lors de l'édition

Si vous travaillez avec une vue de table statique ou que vous ne disposez que d'un nombre connu de UITextViews, vous pouvez potentiellement simplifier l'étape 2 beaucoup plus simplement.

1. Écrasez d’abord 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éfinissez la fonction qui a calculé la hauteur requise:

Ajoutez une NSMutableDictionary (dans cet exemple appelée textViews) en tant que variable d'instance à votre sous-classe UITableViewController.

Utilisez ce dictionnaire pour stocker les références à la personne UITextViews comme ceci:

(et oui, indexPaths sont des 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 suivantes, il est important que le délégué du UITextViews soit défini sur votre UITableViewController. Si vous avez besoin de quelque chose d'autre en tant que délégué, vous pouvez le contourner en passant les appels appropriés à partir de là ou en utilisant les points d'ancrage NSNotificationCenter appropriés.

- (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. Suivre le curseur pendant l'édition

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

Ceci fera défiler la UITableView jusqu'à la position du curseur, si elle ne se trouve pas dans le rect visible 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. Ajustez le rect visible en définissant des incrustations

Pendant l’édition, certaines parties de votre UITableView peuvent être couvertes par le clavier. Si les espaces insérés dans les vues de table ne sont pas ajustés, scrollToCursorForTextView: ne pourra pas faire défiler le curseur jusqu'à votre curseur, s'il se trouve en bas de la table.

- (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 dernière partie:

Dans votre vue s'est chargée, inscrivez-vous pour recevoir les notifications de modifications du clavier via 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];
}

S'il vous plaît ne vous fâchez pas contre moi, pour avoir répondu si longtemps. Bien que tout ne soit pas nécessaire pour répondre à la question, je pense qu’il sera utile d’ajouter d’autres personnes aux problèmes directement liés à ces questions.


MISE À JOUR:

Comme Dave Haupert l'a fait remarquer, j'ai oublié d'inclure la fonction rectVisible:

- (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);
}

J'ai aussi remarqué que scrollToCursorForTextView: incluait toujours une référence directe à l'un des TextFields de mon projet. Si vous avez un problème avec bodyTextView introuvable, vérifiez la version mise à jour de la fonction.

428
Tim Bodeit

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 essentiellement 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 )
37
manecosta

Si vous utilisez UITableViewAutomaticDimension, j'ai une solution très simple (iOS 8 uniquement). Dans mon cas, il s’agit d’une vue de table statique, mais je suppose que vous pourriez l’adapter à des prototypes dynamiques ...

J'ai un exutoire de contrainte pour la hauteur de la vue texte et j'ai implémenté les méthodes suivantes comme ceci:

// Outlets

@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeight;


// Implementation

#pragma mark - Private Methods

- (void)updateTextViewHeight {
    self.textViewHeight.constant = self.textView.contentSize.height + self.textView.contentInset.top + self.textView.contentInset.bottom;
}

#pragma mark - View Controller Overrides

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateTextViewHeight];
}

#pragma mark - TableView Delegate & Datasource

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 80;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

#pragma mark - TextViewDelegate

- (void)textViewDidChange:(UITextView *)textView {
    [self.tableView beginUpdates];
    [self updateTextViewHeight];
    [self.tableView endUpdates];
}

mais rappelez-vous: la vue texte doit être déroulante et vous devez configurer vos contraintes de sorte qu'elles fonctionnent pour la dimension automatique:

  • configurer toutes les vues de la cellule les unes par rapport aux autres, avec des hauteurs fixes (y compris la hauteur de vue du texte, que vous modifierez par programme)
  • la vue la plus haute a l'espacement supérieur et la vue la plus basse a l'espacement inférieur de la vue super;

L'exemple de cellule le plus élémentaire est:

  • pas d'autres vues dans la cellule sauf la vue texte
  • 0 marges autour de tous les côtés de la vue texte et contrainte de hauteur prédéfinie pour la vue texte.
9
dec-vt100

La réponse de Tim Bodeit est excellente. J'ai utilisé le code de solution simple pour obtenir correctement la hauteur de la vue de texte et utiliser cette hauteur dans heightForRowAtIndexPath. Mais je n'utilise pas le reste de la réponse pour redimensionner la vue du texte. Au lieu de cela, j'écris du code pour changer la frame de la vue de texte dans cellForRowAtIndexPath.

Tout fonctionne dans iOS 6 et versions ultérieures, mais dans iOS 7, le texte en affichage texte ne peut pas être entièrement affiché même si la frame de l'affichage texte est bien redimensionnée. (Je n'utilise pas Auto Layout). Cela devrait être la raison pour laquelle dans iOS 7 il y a TextKit et la position du texte est contrôlée par NSTextContainer dans UITextView. Donc, dans mon cas, je dois ajouter une ligne pour définir la someTextView afin de la faire fonctionner correctement dans iOS 7.

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

Comme le dit la documentation, ce que fait cette propriété est:

Détermine si le destinataire ajuste la hauteur de son rectangle de délimitation lorsque sa vue texte est redimensionnée. Valeur par défaut: NO.

Si vous laissez la valeur par défaut, après avoir redimensionné la frame de someTextView, la taille de la textContainer ne sera pas modifiée, ce qui entraînera que le texte ne peut être affiché que dans la zone. avant de redimensionner.

Et peut-être est-il nécessaire de définir le scrollEnabled = NO au cas où il y aurait plus d'un textContainer, de sorte que le texte sera refondu d'un textContainer à un autre.

5
PowerQian

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

Configuration:

  1. Table avec cellules prototypes.
  2. Chaque cellule contient une taille dynamique UITextView avec d'autres contenus.
  3. Les cellules prototypes sont associées à TableCell.h.
  4. UITableView est associé à TableViewController.h.

Solution:

(1) Ajouter à 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 à 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:

Voici donc ce qui se passe: chaque vue de texte est liée à la hauteur des cellules du tableau par des contraintes verticales et horizontales. Cela signifie que lorsque la hauteur de la cellule de tableau augmente, la vue du texte augmente également sa taille. J'ai utilisé une version modifiée du code de @ manecosta pour calculer la hauteur requise d'une vue de texte afin qu'elle corresponde au texte donné dans une cellule. Cela signifie donc que, étant donné un texte avec un nombre X de caractères, frameForText: renverra une taille qui aura une propriété size.height correspondant à la hauteur requise de la vue texte.

Il ne reste plus qu'à mettre à jour la hauteur de la cellule pour qu'elle corresponde à celle de la vue de texte requise. Et ceci est réalisé à heightForRowAtIndexPath:. Comme indiqué dans les commentaires, étant donné que size.height ne représente que la hauteur de la vue texte et non la cellule entière, un décalage doit y être ajouté. Dans le cas de l'exemple, cette valeur était 80.

4
Zorayr

Une approche si vous utilisez l'autolayout est de laisser le moteur d'autolayout calculer la taille pour vous. Ce n’est pas l’approche la plus efficace, mais c’est plutôt pratique (et sans doute la plus précise). Cela devient plus pratique à mesure que la disposition de la cellule se complexifie - par ex. tout à coup, vous avez deux ou plusieurs vues de texte/champs dans la cellule.

J'ai répondu à une question similaire avec un échantillon complet de la taille des cellules tableview à l'aide de la disposition automatique, ici:

Comment redimensionner superview pour s'adapter à toutes les sous-vues avec autolayout?

3
TomSwift

La solution lisse complète est la suivante.

Premièrement, nous avons besoin de la classe de cellules avec un textView

@protocol TextInputTableViewCellDelegate <NSObject>
@optional
- (void)textInputTableViewCellTextWillChange:(TextInputTableViewCell *)cell;
- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell;
@end

@interface TextInputTableViewCell : UITableViewCell
@property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
@property (nonatomic, readonly) UITextView *textView;
@property (nonatomic) NSInteger minLines;
@property (nonatomic) CGFloat lastRelativeFrameOriginY;
@end


#import "TextInputTableViewCell.h"

@interface TextInputTableViewCell () <UITextViewDelegate> {
    NSLayoutConstraint *_heightConstraint;
}
@property (nonatomic) UITextView *textView;
@end

@implementation TextInputTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;

        _textView = [UITextView new];
        _textView.translatesAutoresizingMaskIntoConstraints = NO;
        _textView.delegate = self;
        _textView.scrollEnabled = NO;
        _textView.font = CELL_REG_FONT;
        _textView.textContainer.lineFragmentPadding = 0.0;
        _textView.textContainerInset = UIEdgeInsetsZero;
        [self.contentView addSubview:_textView];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];
        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];

        _heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
                         attribute: NSLayoutAttributeHeight
                         relatedBy: NSLayoutRelationGreaterThanOrEqual
                         toItem: nil
                         attribute: NSLayoutAttributeNotAnAttribute
                         multiplier: 0.0
                         constant: (_textView.font.lineHeight + 15)];
        _heightConstraint.priority = UILayoutPriorityRequired - 1;
        [_textView addConstraint:_heightConstraint];
    }
    return self;
}

- (void)prepareForReuse {
    [super prepareForReuse];    
    self.minLines = 1;
}

- (void)setMinLines:(NSInteger)minLines {
    _heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if ([self.delegate respondsToSelector:@selector(textInputTableViewCellTextWillChange:)]) {
        [self.delegate textInputTableViewCellTextWillChange:self];
    }
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {
    if ([self.delegate respondsToSelector:@selector(textInputTableViewCellTextDidChange:)]) {
        [self.delegate textInputTableViewCellTextDidChange:self];
    }
}

Ensuite, nous l’utilisons dans TableViewController

@interface SomeTableViewController () <TextInputTableViewCellDelegate>
@end

@implementation SomeTableViewController

. . . . . . . . . . . . . . . . . . . .

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    TextInputTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: TextInputTableViewCellIdentifier forIndexPath:indexPath];
    cell.delegate = self;
    cell.minLines = 3;
    . . . . . . . . . .  
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

- (void)textInputTableViewCellWillChange:(TextInputTableViewCell *)cell {
    cell.lastRelativeFrameOriginY = cell.frame.Origin.y - self.tableView.contentOffset.y;
}

- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell {
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    [UIView performWithoutAnimation:^{
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
    }];

    CGFloat contentOffsetY = cell.frame.Origin.y - cell.lastRelativeFrameOriginY;
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);

    CGRect caretRect = [cell.textView caretRectForPosition:cell.textView.selectedTextRange.start];
    caretRect = [self.tableView convertRect:caretRect fromView:cell.textView];

    CGRect visibleRect = self.tableView.bounds;
    visibleRect.Origin.y += self.tableView.contentInset.top;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
    BOOL res = CGRectContainsRect(visibleRect, caretRect);
    if (!res) {
        caretRect.size.height += 5;
        [self.tableView scrollRectToVisible:caretRect animated:NO];
    }
}
@end
  • Ici, minLines permet de définir la hauteur minimale de textView (pour résister à la minimisation de la hauteur par AutoLayout avec UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: avec le même chemin indexPath démarre le calcul et la mise en page de la hauteur de tableViewCell.

  • performWithoutAnimation: supprime les effets secondaires (le décalage de contenu tableView saute au début d'une nouvelle ligne lors de la frappe).

  • Il est important de conserver relativeFrameOriginY (et non contentOffsetY!) Lors de la mise à jour des cellules car contentSize des cellules avant que la cellule actuelle puisse être modifiée de manière inattendue par le calcul autoLayout. Il supprime les sauts visuels sur la césure du système lors de la frappe de mots longs.

  • Notez que vous ne devez pas définir la propriétéestimatedRowHeight! Ce qui suit ne marche pas

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    

    tilisez uniquement la méthode tableViewDelegate.

=============================================== =========================

Si cela ne vous gêne pas de faire une faible liaison entre tableView et tableViewCell et de mettre à jour la géométrie de la tableView depuis tableViewCell, il est possible de mettre à jour TextInputTableViewCell classe ci-dessus:

@interface TextInputTableViewCell : UITableViewCell
@property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
@property (nonatomic, weak) UITableView *tableView;
@property (nonatomic, readonly) UITextView *textView;
@property (nonatomic) NSInteger minLines;
@end


#import "TextInputTableViewCell.h"

@interface TextInputTableViewCell () <UITextViewDelegate> {
    NSLayoutConstraint *_heightConstraint;
    CGFloat _lastRelativeFrameOriginY;
}
@property (nonatomic) UITextView *textView;
@end

@implementation TextInputTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;

        _textView = [UITextView new];
        _textView.translatesAutoresizingMaskIntoConstraints = NO;
        _textView.delegate = self;
        _textView.scrollEnabled = NO;
        _textView.font = CELL_REG_FONT;
        _textView.textContainer.lineFragmentPadding = 0.0;
        _textView.textContainerInset = UIEdgeInsetsZero;
        [self.contentView addSubview:_textView];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];
        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];

        _heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
                         attribute: NSLayoutAttributeHeight
                         relatedBy: NSLayoutRelationGreaterThanOrEqual
                         toItem: nil
                         attribute: NSLayoutAttributeNotAnAttribute
                         multiplier: 0.0
                         constant: (_textView.font.lineHeight + 15)];
        _heightConstraint.priority = UILayoutPriorityRequired - 1;
        [_textView addConstraint:_heightConstraint];
    }
    return self;
}

- (void)prepareForReuse {
    [super prepareForReuse];    
    self.minLines = 1;
    self.tableView = nil;
}

- (void)setMinLines:(NSInteger)minLines {
    _heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

    _lastRelativeFrameOriginY = self.frame.Origin.y - self.tableView.contentOffset.y;
    return YES;
}

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

    NSIndexPath *indexPath = [self.tableView indexPathForCell:self];
    if (indexPath == nil) return;

    [UIView performWithoutAnimation:^{
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
    }];

    CGFloat contentOffsetY = self.frame.Origin.y - _lastRelativeFrameOriginY;
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);

    CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.start];
    caretRect = [self.tableView convertRect:caretRect fromView:self.textView];

    CGRect visibleRect = self.tableView.bounds;
    visibleRect.Origin.y += self.tableView.contentInset.top;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;

    BOOL res = CGRectContainsRect(visibleRect, caretRect);
    if (!res) {
        caretRect.size.height += 5;
        [self.tableView scrollRectToVisible:caretRect animated:NO];
    }
}
@end
1
malex
  1. Placez UILabel derrière votre UITextView.
  2. Utilisez cette réponse: https://stackoverflow.com/a/36054679/6681462 à UILabel que vous avez créé
  3. Donnez-leur les mêmes contraintes et polices
  4. Définissez le même texte;

La hauteur de votre cellule sera calculée en fonction du contenu de UILabel, mais tout le texte sera affiché par TextField.

1
ilnur

Si vous souhaitez ajuster automatiquement la hauteur de UITableViewCell en fonction de la hauteur de la hauteur intérieure de UITextView. Voir ma réponse ici: https://stackoverflow.com/a/45890087/1245231

La solution est assez simple et devrait fonctionner depuis iOS 7. Assurez-vous que l'option Scrolling Enabled est désactivée pour la UITextView dans la UITableViewCell du StoryBoard.

Ensuite, dans viewDidLoad () de votre UITableViewController, définissez les tableView.rowHeight = UITableViewAutomaticDimension et tableView.estimatedRowHeight > 0 tels que:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

C'est ça. La hauteur de UITableViewCell sera automatiquement ajustée en fonction de la hauteur intérieure de UITextView.

0
petrsyn

Version rapide

func textViewHeightForAttributedText(text: NSAttributedString, andWidth width: CGFloat) -> CGFloat {
    let calculationView = UITextView()
    calculationView.attributedText = text
    let size = calculationView.sizeThatFits(CGSize(width: width, height: CGFloat.max))
    return size.height
}
0
Adam Smaka
UITextView *txtDescLandscape=[[UITextView alloc] initWithFrame:CGRectMake(2,20,310,2)];

    txtDescLandscape.editable =NO;
    txtDescLandscape.textAlignment =UITextAlignmentLeft;
    [txtDescLandscape setFont:[UIFont fontWithName:@"ArialMT" size:15]];
    txtDescLandscape.text =[objImage valueForKey:@"imgdescription"];
    txtDescLandscape.text =[txtDescLandscape.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    [txtDescLandscape sizeToFit];
    [headerView addSubview:txtDescLandscape];

    CGRect txtViewlandscpframe = txtDescLandscape.frame;
    txtViewlandscpframe.size.height = txtDescLandscape.contentSize.height;
    txtDescLandscape.frame = txtViewlandscpframe;

je pense que de cette façon, vous pouvez compter la hauteur de votre vue texte, puis redimensionner votre cellule en tant que table, en fonction de cette hauteur, afin que vous puissiez afficher le texte intégral sur une cellule.

0
D-eptdeveloper