UITableViewCell com altura UITextView no iOS 7?


121

Como posso calcular a altura de um UITableViewCell com um UITextView no iOS 7?

Encontrei muitas respostas em perguntas semelhantes, mas sizeWithFont:participei de todas as soluções e esse método foi preterido!

Eu sei que tenho que usar, - (CGFloat)tableView:heightForRowAtIndexPath:mas como faço para calcular a altura que meu TextView precisa para exibir o texto inteiro?

Respostas:


428

Antes de tudo, é muito importante observar que há uma grande diferença entre o UITextView e o UILabel quando se trata de como o texto é renderizado. O UITextView não apenas possui inserções em todas as bordas, mas também o layout do texto dentro dele é um pouco diferente.

Portanto, sizeWithFont:é um caminho ruim para o UITextViews.

Em vez disso, UITextViewele próprio possui uma função chamada sizeThatFits:que retornará o menor tamanho necessário para exibir todo o conteúdo da UITextViewcaixa delimitadora que você pode especificar.

O seguinte funcionará igualmente para as versões iOS 7 e anteriores e, a partir de agora, não inclui nenhum método que foi preterido.


Solução Simples

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

Esta função terá ae NSAttributedStringa largura desejada como ae CGFloatretornará a altura necessária


Solução detalhada

Como fiz recentemente algo semelhante, pensei em compartilhar algumas soluções para os problemas conectados que encontrei. Espero que ajude alguém.

Isso é muito mais profundo e abrangerá o seguinte:

  • Obviamente: definir a altura de um com UITableViewCellbase no tamanho necessário para exibir o conteúdo completo de um contidoUITextView
  • Responder a alterações de texto (e animar as alterações de altura da linha)
  • Mantendo o cursor dentro da área visível e mantendo o primeiro respondedor UITextViewao redimensionar UITableViewCellenquanto edita

Se você estiver trabalhando com uma exibição de tabela estática ou tiver apenas um número conhecido de UITextViews, poderá potencialmente tornar a etapa 2 muito mais simples.

1. Primeiro, substitua o 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. Defina a função que calculou a altura necessária:

Adicione um NSMutableDictionary(neste exemplo chamado textViews) como uma variável de instância à sua UITableViewControllersubclasse.

Use este dicionário para armazenar referências à pessoa da seguinte UITextViewsmaneira:

(e sim, indexPaths são chaves válidas para dicionários )

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

Esta função agora calcula a altura real:

- (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. Ative o redimensionamento durante a edição

Para as próximas duas funções, é importante que o delegado da UITextViewsseja definido como seu UITableViewController. Se você precisar de outra coisa como delegado, poderá contorná-lo fazendo as chamadas relevantes a partir daí ou usando os ganchos apropriados do NSNotificationCenter.

- (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. Siga o cursor enquanto edita

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

Isso fará a UITableViewrolagem para a posição do cursor, se não estiver dentro do Rect visível do 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. Ajuste o retângulo visível, definindo inserções

Durante a edição, partes do seu UITableViewpodem ser cobertas pelo teclado. Se as inserções das visualizações de tablaturas não forem ajustadas, scrollToCursorForTextView:não será possível rolar para o cursor, se estiver na parte inferior da visualização de tablatura.

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

E última parte:

Dentro da sua visualização, carregue, inscreva-se nas notificações para alterações do teclado através de 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];
}

Por favor, não fique bravo comigo, por fazer essa resposta por tanto tempo. Embora nem tudo seja necessário para responder à pergunta, acredito que há outras pessoas para quem essas questões diretamente relacionadas serão úteis.


ATUALIZAR:

Como Dave Haupert apontou, eu esqueci de incluir a rectVisiblefunção:

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

Também notei que isso scrollToCursorForTextView:ainda incluía uma referência direta a um dos TextFields no meu projeto. Se você tiver um problema em bodyTextViewnão ser encontrado, verifique a versão atualizada da função.


1
Esse código está funcionando bem! Redimensiona tudo! Mas, meu TextView sempre obtém uma altura de 30px! Existem configurações que não tenho permissão para definir ou há algo que não tenho permissão no UITextView?
MyJBMe 19/09/13

1
Esta solução parece não funcionar em copiar e colar se o texto for grande, alguma idéia?
Vikings

2
@ Tim Bodeit, sua solução funciona, obrigado! Mas acho que você deve observar no comentário que atribuir attributedTextsem especificar fonte, cor e alinhamento de texto leva à configuração dos valores padrão dos atributos NSAttributedString para o textView. No meu caso, isso causa diferentes alturas da visualização de texto para o mesmo texto.
22413 Alexander

4
Esta é uma das minhas respostas favoritas de estouro de pilha de todos os tempos - obrigado!
Richard Venable

3
@ TimBodeit: Não consigo fazer isso funcionar no iOS8. Por favor, deixe-me saber como isso pode ser corrigido.
Arun Gupta

37

Há uma nova função para substituir sizeWithFont, que é boundingRectWithSize.

Eu adicionei a seguinte função ao meu projeto, que utiliza a nova função no iOS7 e a antiga no iOS menor que 7. Ela tem basicamente a mesma sintaxe 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
        }
    }

Você pode adicionar esse IOS_NEWER_OR_EQUAL_TO_7 no seu arquivo prefix.pch no seu projeto como:

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

Meus UITextViews ainda não são redimensionados muito bem e tornam-se roláveis ​​quando o texto se estende por três linhas; pastebin.com/Wh6vmBqh
Martin de Keijzer 12/09

A segunda declaração de retorno também lança um aviso de descontinuação no XCode.
Martin de Keijzer 12/09

Você também está definindo o tamanho do UItextView para o tamanho calculado do texto, em cellForRowAtIndexPath? Além disso, você não deve se preocupar com o aviso no segundo retorno, pois ele é usado apenas quando o aplicativo é executado em um dispositivo iOS6 no qual a função não está obsoleta.
manecosta 13/09/13

Você pode fornecer um exemplo simples de como usar essa função?
precisa saber é o seguinte

A documentação da @manecosta da Apple diz que você deve "limitar" o resultado: no iOS 7 e posterior, esse método retorna tamanhos fracionários (no componente de tamanho do CGRect retornado); Para usar um tamanho retornado para visualizações de tamanho, você deve aumentar seu valor para o número inteiro mais alto mais próximo usando a função ceil.
HpTerm

9

Se você estiver usando UITableViewAutomaticDimension, eu tenho uma solução muito simples (somente iOS 8). No meu caso, é uma tabela estática, mas acho que você pode adaptar isso para protótipos dinâmicos ...

Eu tenho uma saída de restrição para a altura da exibição de texto e implementei os seguintes métodos como este:

// 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];
}

Mas lembre-se : a visualização do texto deve ser rolável e você deve configurar suas restrições para que funcionem na dimensão automática:

  • configure toda a visualização na célula em relação uma à outra, com alturas fixas (incluindo a altura da visualização de texto, que você mudará programaticamente)
  • a vista superior tem o espaçamento superior e a vista inferior exibe o espaçamento inferior à super vista;

O exemplo mais básico de célula é:

  • nenhuma outra visualização na célula, exceto a visualização de texto
  • 0 margens em todos os lados da visualização de texto e uma restrição de altura predefinida para a visualização de texto.

1
a exibição de texto devem NÃO ser de rolagem
Akshit Zaveri

Estou recebendo o mesmo tamanho em updateTextviewHeight o tempo todo. Parece que o tamanho do conteúdo está errado. A rolagem está desativada.
Dvole

5

A resposta de Tim Bodeit é ótima. Usei o código do Simple Solution para obter corretamente a altura da exibição de texto e usá-la heightForRowAtIndexPath. Mas não uso o restante da resposta para redimensionar a exibição de texto. Em vez disso, escrevo código para alterar a frameexibição do texto cellForRowAtIndexPath.

Tudo está funcionando no iOS 6 e abaixo, mas no iOS 7 o texto na exibição de texto não pode ser totalmente exibido, mesmo que a frameexibição de texto seja redimensionada. (Eu não estou usando Auto Layout). Deve ser o motivo pelo qual o iOS 7 existe TextKite a posição do texto é controlada por NSTextContainerin UITextView. Portanto, no meu caso, preciso adicionar uma linha para definir o someTextViewfim de fazê-lo funcionar corretamente no iOS 7.

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

Como a documentação disse, o que essa propriedade faz é:

Controla se o receptor ajusta a altura do retângulo delimitador quando a exibição do texto é redimensionada. Valor padrão: NÃO.

Se deixá-lo com o valor padrão, após redimensionar o framede someTextView, o tamanho dotextContainer não será alterado, levando ao resultado de que o texto só poderá ser exibido na área antes de redimensionar.

E talvez seja necessário definir o scrollEnabled = NOcaso de haver mais de um textContainer, para que o texto reflita de um textContainerpara o outro.


4

Aqui está mais uma solução que visa a simplicidade e a criação rápida de protótipos :

Configuração:

  1. Tabela com células protótipo.
  2. Cada célula contém tamanho dinâmico UITextView com outros conteúdos.
  3. As células protótipo estão associadas TableCell.h.
  4. UITableViewestá associado a TableViewController.h.

Solução:

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

Explicação:

Portanto, o que está acontecendo aqui é o seguinte: cada visualização de texto é vinculada à altura das células da tabela por restrições verticais e horizontais - ou seja, quando a altura da célula da tabela aumenta, a visualização do texto também aumenta seu tamanho. Usei uma versão modificada do código de @ manecosta para calcular a altura necessária de uma exibição de texto para ajustar o texto fornecido em uma célula. Isso significa que, dado um texto com o número X de caracteres, frameForText:retornará um tamanho que terá uma propriedade size.heightque corresponda à altura necessária da exibição de texto.

Agora, resta apenas atualizar a altura da célula para corresponder à altura da visualização de texto necessária. E isso é alcançado em heightForRowAtIndexPath:. Conforme observado nos comentários, como size.heightexiste apenas a altura da exibição do texto e não a célula inteira, deve haver algum deslocamento adicionado a ela. No caso do exemplo, esse valor foi 80.


O que significa 'dream.dream'?
precisa saber é o seguinte

@MyJBMe desculpe por fazer parte do meu próprio projeto - atualizei o código de acordo. dream.dreamera o texto que eu estava renderizando na exibição de texto.
Zorayr 22/10/2013

3

Uma abordagem, se você estiver usando o pagamento automático, é permitir que o mecanismo do pagamento automático calcule o tamanho para você. Essa não é a abordagem mais eficiente, mas é bastante conveniente (e sem dúvida a mais precisa). Torna-se mais conveniente à medida que a complexidade do layout da célula aumenta - por exemplo, de repente você tem dois ou mais campos de texto / texto na célula.

Respondi a uma pergunta semelhante com uma amostra completa para dimensionar células da tableview usando o layout automático, aqui:

Como redimensionar a superview para caber em todas as subviews com o autolayout?


1

A solução completa e suave é a seguinte.

Primeiro, precisamos da classe cell com um 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];
    }
}

Em seguida, usamos no 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
  • Aqui minLinespermite definir a altura mínima para o textView (para resistir à minimização da altura pelo AutoLayout com UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: com o mesmo indexPath inicia o recálculo e o layout da altura do tableViewCell.

  • performWithoutAnimation: remove o efeito colateral (deslocamento do conteúdo do tableView pulando ao iniciar uma nova linha enquanto digita).

  • É importante preservar relativeFrameOriginY(não contentOffsetY!) Durante a atualização da célula devido contentSizeàs células antes que a célula atual possa ser alterada pelo cálculo do AutoLayout de maneira inesperada. Remove saltos visuais na hifenização do sistema enquanto digita palavras longas.

  • Observe que você não deve definir a propriedade estimatedRowHeight ! O seguinte não funciona

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    Use apenas o método tableViewDelegate.

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

Se alguém não se importa com a ligação fraca entre tableView e tableViewCell e com a atualização da geometria do tableView de tableViewCell , é possível atualizar a TextInputTableViewCellclasse acima:

@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
  1. Coloque o UILabel atrás do seu UITextView.
  2. Use esta resposta: https://stackoverflow.com/a/36054679/6681462 para o UILabel que você criou
  3. Dê a eles as mesmas restrições e fontes
  4. Defina o mesmo texto para eles;

A altura da sua célula será calculada pelo conteúdo do UILabel, mas todo o texto será mostrado pelo TextField.


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;

Eu acho que dessa forma, você pode contar a altura da sua exibição de texto e redimensionar sua célula de tableview de acordo com essa altura, para que você possa mostrar o texto completo na célula


0

Versão 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
}

0

Se você deseja ajustar automaticamente UITableViewCella altura da base, com base na altura da altura interna UITextView. Veja minha resposta aqui: https://stackoverflow.com/a/45890087/1245231

A solução é bastante simples e deve funcionar desde o iOS 7. Verifique se a Scrolling Enabledopção está desativada no UITextViewinterior UITableViewCelldo StoryBoard.

Em viewDidLoad () do seu UITableViewController, defina o tableView.rowHeight = UITableViewAutomaticDimensione tableView.estimatedRowHeight > 0como:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

É isso aí. UITableViewCellA altura da peça será ajustada automaticamente com base na UITextViewaltura da parte interna .


-2

Para iOS 8 e superior, você pode simplesmente usar

your_tablview.estimatedrowheight= minheight você quer

your_tableview.rowheight=UItableviewautomaticDimension
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.