UITableViewCell avec la hauteur UITextView dans iOS 7?

121

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 obsolète!

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

MyJBMe
la source

Réponses:

428

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. Non seulement UITextView a des encarts sur toutes les bordures, mais la disposition du texte à l'intérieur est également légèrement différente.

Par conséquent, sizeWithFont:c'est une mauvaise façon d'aller pour UITextViews.

Au lieu de cela, UITextViewelle a une fonction appelée sizeThatFits:qui retournera la plus petite taille nécessaire pour afficher tout le contenu de l' UITextViewintérieur d'une boîte englobante, que vous pouvez spécifier.

Ce qui suit fonctionnera de la même manière pour iOS 7 et les versions antérieures et n'inclut pour le moment aucune méthode 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 prendra a NSAttributedStringet la largeur désirée comme a CGFloatet retournera la hauteur nécessaire


Solution détaillée

Depuis que j'ai récemment fait quelque chose de similaire, j'ai pensé partager également quelques solutions aux problèmes liés que j'ai rencontrés. J'espère que cela aidera quelqu'un.

Ceci est beaucoup plus détaillé et couvrira les points suivants:

  • Bien sûr: définir la hauteur d'un en UITableViewCellfonction de la taille nécessaire pour afficher le contenu complet d'un contenuUITextView
  • Répondre aux changements de texte (et animer les changements de hauteur de la ligne)
  • Garder le curseur à l'intérieur de la zone visible et garder le premier répondant sur le UITextViewlors du redimensionnement de la UITableViewCellpendant l'édition

Si vous travaillez avec une vue de table statique ou si vous n'avez qu'un nombre connu de UITextViews, vous pouvez potentiellement simplifier l'étape 2.

1. Tout d'abord, écrasez 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 nécessaire:

Ajoutez un NSMutableDictionary(dans cet exemple appelé textViews) comme variable d'instance à votre UITableViewControllersous - classe.

Utilisez ce dictionnaire pour stocker les références à l'individu UITextViewscomme 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 la modification

Pour les deux fonctions suivantes, il est important que le délégué de UITextViewssoit défini sur votre UITableViewController. Si vous avez besoin d'autre chose en tant que délégué, vous pouvez le contourner en effectuant les appels appropriés à partir de là ou en utilisant les hooks 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. Suivez le curseur lors de l'édition

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

Cela fera UITableViewdéfiler jusqu'à la position du curseur, s'il n'est pas à l'intérieur du Rect visible de l'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 encarts

Lors de l'édition, certaines parties de votre UITableViewpeuvent être couvertes par le clavier. Si les insertions de tableview ne sont pas ajustées, scrollToCursorForTextView:ne pourra pas faire défiler jusqu'à votre curseur, s'il se trouve 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 dernière partie:

À l'intérieur de votre vue s'est chargée, inscrivez-vous aux notifications pour les 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 rendu cette réponse si longue. Même si tout n'est pas nécessaire pour répondre à la question, je crois qu'il y a d'autres personnes à qui ces questions directement liées seront utiles.


METTRE À JOUR:

Comme l'a souligné Dave Haupert, j'ai oublié d'inclure la rectVisiblefonction:

- (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 également remarqué que cela scrollToCursorForTextView:incluait toujours une référence directe à l'un des TextFields de mon projet. Si vous rencontrez un problème pour bodyTextViewne pas être trouvé, vérifiez la version mise à jour de la fonction.

Tim Bodeit
la source
1
Ce code fonctionne bien du tout! Il redimensionne tout! Mais, mon TextView obtient toujours une hauteur de 30px! Y a-t-il des paramètres que je ne suis pas autorisé à définir, ou y a-t-il quelque chose que je ne suis pas autorisé dans UITextView?
MyJBMe
1
Cette solution ne semble pas fonctionner sur un copier-coller si le texte est volumineux, des idées?
Vikings
2
@Tim Bodeit, votre solution fonctionne, merci! Mais je pense que vous devriez noter dans le commentaire que l'attribution de attributedTextsans spécifier l'alignement de la police, de la couleur et du texte conduit à la définition des valeurs par défaut des attributs NSAttributedString au textView. Dans mon cas, cela provoque l'obtention de différentes hauteurs de la vue de texte pour le même texte.
Alexander
4
C'est l'une de mes réponses Stack Overflow préférées de tous les temps - merci!
Richard Venable
3
@TimBodeit: Je ne parviens pas à faire fonctionner cela sur iOS8. S'il vous plaît laissez-moi savoir comment cela peut être résolu.
Arun Gupta
37

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 ce IOS_NEWER_OR_EQUAL_TO_7 sur votre fichier prefix.pch dans votre projet comme:

#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )
manecosta
la source
Mes UITextViews ne sont toujours pas bien mis à l'échelle et deviennent défilables lorsque le texte s'étend sur 3 lignes; pastebin.com/Wh6vmBqh
Martin de Keijzer
La deuxième instruction return lève également un avertissement d'obsolescence dans XCode.
Martin de Keijzer
Définissez-vous également la taille de UItextView sur la taille calculée du texte, dans cellForRowAtIndexPath? Vous ne devriez pas non plus vous inquiéter de l'avertissement dans le deuxième retour car il n'est utilisé que lorsque l'application est exécutée sur un appareil iOS6 dans lequel la fonction n'est pas obsolète.
manecosta
Pouvez-vous fournir un exemple simple d'utilisation de cette fonction?
Zorayr
@manecosta La documentation d'Apple indique que vous devez «plafonner» le résultat: dans iOS 7 et les versions ultérieures, cette méthode renvoie des tailles fractionnaires (dans le composant de taille du CGRect renvoyé); pour utiliser une taille renvoyée pour dimensionner les vues, vous devez utiliser augmenter sa valeur à l'entier supérieur le plus proche à l'aide de la fonction ceil.
HpTerm
9

Si vous utilisez UITableViewAutomaticDimension, j'ai une solution très simple (iOS 8 uniquement). Dans mon cas, c'est une vue de table statique, mais je suppose que vous pouvez l'adapter pour des prototypes dynamiques ...

J'ai une sortie de contrainte pour la hauteur de la vue de 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 pouvoir faire défiler, et vous devez configurer vos contraintes de manière à ce qu'elles fonctionnent pour la cote automatique:

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

L'exemple de cellule le plus basique est:

  • aucune autre vue dans la cellule à l'exception de la vue de texte
  • 0 marges autour de tous les côtés de la vue de texte et une contrainte de hauteur prédéfinie pour la vue de texte.
déc-vt100
la source
1
la vue du texte ne doit PAS être défilante
Akshit Zaveri
J'obtiens la même taille sous updateTextviewHeight tout le temps. La taille du contenu semble incorrecte. Le défilement est désactivé.
Dvole
5

La réponse de Tim Bodeit est excellente. J'ai utilisé le code de Simple Solution 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 texte. Au lieu de cela, j'écris du code pour changer la framevue du texte dans cellForRowAtIndexPath.

Tout fonctionne dans iOS 6 et les versions antérieures, mais dans iOS 7, le texte en vue texte ne peut pas être entièrement affiché, même si la framevue de texte est effectivement redimensionnée. (Je n'utilise pas Auto Layout). Cela devrait être la raison pour laquelle dans iOS 7 il y a TextKitet la position du texte est contrôlée par NSTextContainerin UITextView. Donc, dans mon cas, je dois ajouter une ligne pour définir le someTextViewafin de le 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:

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

Si vous la laissez avec la valeur par défaut, après le redimensionnement du framede someTextView, la taille du textContainern'est pas modifiée, conduisant au résultat que le texte ne peut être affiché que dans la zone avant le redimensionnement.

Et peut-être est-il nécessaire de définir le scrollEnabled = NOcas où il y en aurait plus d'un textContainer, afin que le texte soit redistribué de l'un textContainerà l'autre.

PowerQian
la source
4

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

Installer:

  1. Table avec cellules prototypes.
  2. Chaque cellule contient une taille dynamique UITextViewavec d'autres contenus.
  3. Les cellules prototypes sont associées à TableCell.h.
  4. UITableViewest 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:

Donc, ce qui se passe ici est le suivant: 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 cellule du tableau augmente, la vue de 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 pour s'adapter au texte donné dans une cellule. Cela signifie qu'un texte avec un nombre X de caractères frameForText:retournera une taille qui aura une propriété size.heightqui correspond à la hauteur requise de la vue du texte.

Il ne reste plus qu'à mettre à jour la hauteur de la cellule pour qu'elle corresponde à la hauteur de la vue texte requise. Et ceci est réalisé à heightForRowAtIndexPath:. Comme indiqué dans les commentaires, comme il size.heightne s'agit que de la hauteur de la vue texte et non de la cellule entière, il devrait y avoir un décalage ajouté. Dans le cas de l'exemple, cette valeur était de 80.

Zorayr
la source
Que signifie ce «dream.dream»?
MyJBMe
@MyJBMe désolé que cela faisait partie de mon propre projet - j'ai mis à jour le code en conséquence. dream.dreamétait le texte que je rendais dans la vue texte.
Zorayr
3

Une approche si vous utilisez la mise en page automatique consiste à laisser le moteur de mise en page automatique calculer la taille pour vous. Ce n'est pas l'approche la plus efficace, mais elle est assez pratique (et sans doute la plus précise). Cela devient plus pratique à mesure que la complexité de la disposition de la cellule augmente - par exemple, vous avez soudainement deux vues / champs de texte ou plus dans la cellule.

J'ai répondu à une question similaire avec un exemple complet de dimensionnement des cellules de vue de tableau à l'aide de la disposition automatique, ici:

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

TomSwift
la source
1

La solution lisse complète est la suivante.

Tout d'abord, nous avons besoin de la classe de cellule 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 le 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, minLinespermet de définir la hauteur minimale pour le textView (pour résister à la réduction de la hauteur par AutoLayout avec UITableViewAutomaticDimension).

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

  • performWithoutAnimation: supprime l'effet secondaire (décalage de contenu tableView sautant au début d'une nouvelle ligne lors de la saisie).

  • Il est important de conserver relativeFrameOriginY(pas contentOffsetY!) Pendant la mise à jour de la cellule en raison contentSizedes cellules avant que la cellule actuelle ne puisse être modifiée par le calcul de mise en page automatique de manière inattendue. Il supprime les sauts visuels sur la césure du système lors de la saisie de mots longs.

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

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    Utilisez uniquement la méthode tableViewDelegate.

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

Si cela ne vous dérange pas de la liaison faible entre tableView et tableViewCell et de la mise à jour de la géométrie de la tableView à partir de tableViewCell , il est possible de mettre à niveau la TextInputTableViewCellclasse 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
malex
la source
1
  1. Mettez 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. Mettez-leur le même texte;

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

ilnur
la source
0
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 de texte, puis redimensionner votre cellule de vue de tableau en fonction de cette hauteur afin que vous puissiez afficher le texte intégral sur la cellule

Développeur D-ept
la source
0

Version Swift

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
}
Adam Smaka
la source
0

Si vous souhaitez ajuster automatiquement UITableViewCellla hauteur de la base en fonction de la hauteur de la hauteur intérieure 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' Scrolling Enabledoption est désactivée pour l' UITextViewintérieur UITableViewCelldu StoryBoard.

Ensuite, dans viewDidLoad () de votre UITableViewController, définissez le tableView.rowHeight = UITableViewAutomaticDimensionet tableView.estimatedRowHeight > 0tel que:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

C'est tout. UITableViewCellLa hauteur UITextViewde l ' intérieur sera automatiquement ajustée en fonction de la hauteur intérieure .

Petrsyn
la source
-2

Pour iOS 8 et supérieur, vous pouvez simplement utiliser

your_tablview.estimatedrowheight= minheight tu veux

your_tableview.rowheight=UItableviewautomaticDimension
Sablonneux
la source