UITableViewCell с высотой UITextView в iOS 7?

121

Как я могу рассчитать высоту UITableViewCell с UITextView в нем в iOS 7?

Я нашел много ответов на похожие вопросы, но sizeWithFont:принимаю участие в каждом решении, и этот метод устарел!

Я знаю, что должен использовать, - (CGFloat)tableView:heightForRowAtIndexPath:но как мне рассчитать высоту, необходимую моему TextView для отображения всего текста?

MyJBMe
источник

Ответы:

428

Прежде всего, очень важно отметить, что существует большая разница между UITextView и UILabel, когда речь идет о том, как отображается текст. У UITextView не только есть вставки на всех границах, но и макет текста внутри него немного отличается.

Следовательно, sizeWithFont:это плохой вариант для UITextViews.

Вместо этого UITextViewсама вызывается функция, sizeThatFits:которая возвращает наименьший размер, необходимый для отображения всего содержимого UITextViewвнутри ограничивающей рамки, которую вы можете указать.

Следующее будет работать одинаково как для iOS 7, так и для более старых версий, и на данный момент не включает никаких устаревших методов.


Простое решение

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

Эта функция примет a NSAttributedStringи желаемую ширину как a CGFloatи вернет необходимую высоту.


Подробное решение

Поскольку я недавно сделал нечто подобное, я подумал, что также поделюсь некоторыми решениями связанных проблем, с которыми я столкнулся. Надеюсь, это кому-нибудь поможет.

Это гораздо более подробно и будет охватывать следующее:

  • Конечно: установка высоты на UITableViewCellоснове размера, необходимого для отображения полного содержимого содержащегосяUITextView
  • Реагировать на изменения текста (и анимировать изменение высоты строки)
  • Удерживая курсор внутри видимой области и удерживая первого респондента на UITextViewзначке при изменении размера во UITableViewCellвремя редактирования

Если вы работаете со статическим табличным представлением или у вас есть только известное количество UITextViews, вы потенциально можете значительно упростить шаг 2.

1. Сначала перезапишите 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. Определите функцию, которая вычисляет необходимую высоту:

Добавьте NSMutableDictionary(в этом примере называется textViews) в качестве переменной экземпляра в свой UITableViewControllerподкласс.

Используйте этот словарь для хранения ссылок на человека UITextViewsследующим образом:

(и да, indexPaths - действительные ключи для словарей )

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

Эта функция теперь рассчитает фактическую высоту:

- (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. Включите изменение размера во время редактирования.

Для следующих двух функций важно, чтобы делегат был UITextViewsустановлен на ваш UITableViewController. Если вам нужно что-то еще в качестве делегата, вы можете обойти это, сделав соответствующие вызовы оттуда или используя соответствующие перехватчики 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. Следите за курсором во время редактирования

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

Это заставит UITableViewпрокрутку перейти к позиции курсора, если он не находится внутри видимого Rect 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. Отрегулируйте видимый прямоугольник, установив вставки.

Во время редактирования часть вашего UITableViewтекста может быть закрыта клавиатурой. Если вставки tableviews не скорректированы, scrollToCursorForTextView:вы не сможете прокручивать курсор до вашего курсора, если он находится в нижней части 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];
}

И последняя часть:

Внутри вашего представления, которое действительно загрузилось, подпишитесь на уведомления об изменениях клавиатуры через 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];
}

Пожалуйста, не сердитесь на меня за такой длинный ответ. Хотя не все это необходимо для ответа на вопрос, я считаю, что есть другие люди, которым эти вопросы, напрямую связанные с этим, будут полезны.


ОБНОВИТЬ:

Как заметил Дэйв Хауперт, я забыл включить 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);
}

Также я заметил, что scrollToCursorForTextView:в моем проекте все еще есть прямая ссылка на одно из текстовых полей. Если у вас возникла проблема с тем, что вас bodyTextViewне нашли, проверьте обновленную версию функции.

Тим Бодейт
источник
1
Этот код вообще хорошо работает! Он изменяет размер всего! Но мой TextView всегда имеет высоту 30 пикселей! Есть ли настройки, которые мне нельзя устанавливать, или есть что-то, что мне не разрешено в UITextView?
MyJBMe
1
Это решение, похоже, не работает при копировании и вставке, если текст большой, есть идеи?
Vikings
2
@ Тим Бодейт, ваше решение работает, спасибо! Но я думаю, вы должны отметить в комментарии, что назначение attributedTextбез указания шрифта, цвета и выравнивания текста приводит к установке значений по умолчанию для атрибутов NSAttributedString для textView. В моем случае это приводит к получению разной высоты текстового представления для одного и того же текста.
Александр
4
Это один из моих любимых ответов о переполнении стека - спасибо!
Ричард Венейбл,
3
@TimBodeit: я не могу заставить это работать на iOS8. Пожалуйста, дайте мне знать, как это можно исправить.
Арун Гупта
37

Существует новая функция для замены sizeWithFont, которая является boundingRectWithSize.

Я добавил в свой проект следующую функцию, которая использует новую функцию на iOS7 и старую на iOS ниже 7. Она имеет в основном тот же синтаксис, что и 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
        }
    }

Вы можете добавить этот IOS_NEWER_OR_EQUAL_TO_7 в свой файл prefix.pch в своем проекте как:

#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )
manecosta
источник
Мои UITextView по-прежнему плохо масштабируются и становятся прокручиваемыми, когда текст занимает 3 строки; pastebin.com/Wh6vmBqh
Мартин де Кейзер
Второй оператор return также выдает предупреждение об устаревании в XCode.
Мартин де Кейзер
Вы также устанавливаете размер UItextView на рассчитанный размер текста в cellForRowAtIndexPath? Также не стоит беспокоиться о предупреждении во втором возврате, поскольку оно используется только тогда, когда приложение запущено на устройстве iOS6, в котором функция не является устаревшей.
manecosta
Можете ли вы привести простой пример использования этой функции?
Зорайр
@manecosta В документации Apple говорится, что вы должны «засечь» результат: в iOS 7 и более поздних версиях этот метод возвращает дробные размеры (в компоненте размера возвращаемого CGRect); чтобы использовать возвращаемый размер для отображения размера, вы должны использовать его значение для увеличения до ближайшего большего целого числа с помощью функции ceil.
HpTerm 01
9

Если вы используете UITableViewAutomaticDimension, у меня есть действительно простое (только для iOS 8) решение. В моем случае это статическая таблица, но я думаю, вы могли бы адаптировать ее для динамических прототипов ...

У меня есть выход ограничения для высоты текстового представления, и я реализовал следующие методы, подобные этому:

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

Но помните : текстовое представление должно быть прокручиваемым, и вы должны настроить свои ограничения так, чтобы они работали для автоматического измерения:

  • настроить все представления в ячейке относительно друг друга с фиксированной высотой (включая высоту текстового представления, которую вы измените программно)
  • самый верхний вид имеет верхний интервал, а самый нижний вид имеет нижний интервал по отношению к супервизору;

Самый простой пример ячейки:

  • нет других представлений в ячейке, кроме текстового представления
  • 0 полей вокруг всех сторон текстового представления и предопределенное ограничение высоты для текстового представления.
дец-VT100
источник
1
текстовый вид НЕ
ДОЛЖЕН
У меня все время под updateTextviewHeight размер один и тот же. Похоже, размер содержимого неправильный. Прокрутка отключена.
Dvole 05
5

Ответ Тима Бодейта великолепен. Я использовал код Simple Solution, чтобы правильно получить высоту текстового представления и использовать эту высоту в heightForRowAtIndexPath. Но я не использую остальную часть ответа для изменения размера текстового представления. Вместо этого я пишу код, чтобы изменить frameтекстовое представление cellForRowAtIndexPath.

Все работает в iOS 6 и ниже, но в iOS 7 текст в текстовом представлении не может быть полностью отображен, даже если frameразмер текстового представления действительно изменен. (Не пользуюсь Auto Layout). Это должно быть причиной того, что в iOS 7 есть TextKitи положение текста контролируется NSTextContainerin UITextView. Поэтому в моем случае мне нужно добавить строку для установки someTextView, чтобы она работала правильно в iOS 7.

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

Как сказано в документации, это свойство делает следующее:

Определяет, регулирует ли получатель высоту своего ограничивающего прямоугольника при изменении размера его текстового представления. Значение по умолчанию: NO.

Если оставить его значение по умолчанию, после того, как изменить размер frameиз someTextView, размер textContainerне изменяется, что приводит к тому , что текст может отображаться только в области перед изменением размеров.

И, возможно, необходимо установить, scrollEnabled = NOесли их больше одного textContainer, чтобы текст перекомпоновывался с одного textContainerна другой.

PowerQian
источник
4

Вот еще одно решение, направленное на простоту и быстрое прототипирование :

Настроить:

  1. Таблица с прототипами ячеек.
  2. Каждая ячейка содержит динамический размер UITextViewс другим содержимым.
  3. Ячейки-прототипы связаны с TableCell.h.
  4. UITableViewсвязан с TableViewController.h.

Решение:

(1) Добавить в 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) Добавить в 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;
}

Объяснение:

Итак, что здесь происходит: каждое текстовое представление привязано к высоте ячеек таблицы вертикальными и горизонтальными ограничениями - это означает, что, когда высота ячейки таблицы увеличивается, текстовое представление также увеличивает свой размер. Я использовал модифицированную версию кода @manecosta, чтобы вычислить необходимую высоту текстового представления, чтобы она соответствовала заданному тексту в ячейке. Это означает, что для текста с числом символов X frameForText:будет возвращен размер, который будет иметь свойство size.height, соответствующее требуемой высоте текстового представления.

Теперь все, что осталось, - это обновить высоту ячейки, чтобы она соответствовала требуемой высоте текстового представления. И это достигается при heightForRowAtIndexPath:. Как отмечено в комментариях, поскольку size.heightэто только высота для текстового представления, а не для всей ячейки, к нему должно быть добавлено некоторое смещение. В случае примера это значение было 80.

Zorayr
источник
Что означает эта мечта?
MyJBMe
@MyJBMe, извините, это было частью моего собственного проекта - я соответствующим образом обновил код. dream.dreamбыл текст, который я отображал в текстовом представлении.
Зорайр
3

Один из подходов, если вы используете автоматическое размещение, - позволить механизму автоматического размещения вычислить размер за вас. Это не самый эффективный подход, но он довольно удобный (и, возможно, самый точный). Это становится более удобным по мере роста сложности макета ячейки - например, внезапно у вас есть два или более текстовых представления / поля в ячейке.

Я ответил на аналогичный вопрос с полным образцом для определения размера ячеек tableview с использованием автоматического макета, здесь:

Как изменить размер супервизора, чтобы он соответствовал всем подпредставлениям с автоматическим раскладкой?

TomSwift
источник
1

Полное гладкое решение выглядит следующим образом.

Во-первых, нам нужен класс ячейки с 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];
    }
}

Далее мы используем его в 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
  • Здесь minLinesпозволяет установить минимальную высоту для textView (чтобы противостоять минимизации высоты с помощью AutoLayout с помощью UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: с тем же indexPath запускает пересчет высоты tableViewCell и перекомпоновку.

  • performWithoutAnimation: удаляет побочный эффект (смещение содержимого tableView прыгает при начале новой строки при вводе).

  • Важно сохранить relativeFrameOriginY(а не contentOffsetY!) Во время обновления ячеек, потому contentSizeчто ячейки до того, как текущая ячейка может быть изменена с помощью вычисления autoLayout неожиданным образом. Он устраняет визуальные скачки при расстановке переносов в системе при наборе длинных слов.

  • Обратите внимание, что вы не должны устанавливать свойство estimatedRowHeight ! Следующее не работает

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    Используйте только метод tableViewDelegate.

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

Если вы не против слабой привязки между tableView и tableViewCell и обновления геометрии tableView из tableViewCell , можно обновить TextInputTableViewCellкласс выше:

@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
источник
1
  1. Поместите UILabel позади вашего UITextView.
  2. Используйте этот ответ: https://stackoverflow.com/a/36054679/6681462 в UILabel, который вы создали
  3. Дайте им те же ограничения и шрифты
  4. Установите для них одинаковый текст;

Высота вашей ячейки будет рассчитываться по содержимому UILabel, но весь текст будет отображаться с помощью 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;

Я думаю, что таким образом вы можете подсчитать высоту своего текстового представления, а затем изменить размер ячейки табличного представления в соответствии с этой высотой, чтобы вы могли отображать полный текст в ячейке

Д-eptdeveloper
источник
0

Свифт версия

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

Если вы хотите автоматически регулировать UITableViewCellвысоту в зависимости от высоты внутренней UITextViewвысоты. См. Мой ответ здесь: https://stackoverflow.com/a/45890087/1245231

Решение довольно простое и должно работать, начиная с iOS 7. Убедитесь, что этот Scrolling Enabledпараметр отключен для UITextViewвнутренней части UITableViewCellв StoryBoard.

Затем в вашем UITableViewController viewDidLoad () установите tableView.rowHeight = UITableViewAutomaticDimensionи, tableView.estimatedRowHeight > 0например:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

Вот и все. UITableViewCellВысота будет автоматически отрегулирована в зависимости от внутренней UITextViewвысоты.

petrsyn
источник
-2

Для iOS 8 и выше вы можете просто использовать

your_tablview.estimatedrowheight= minheight вы хотите

your_tableview.rowheight=UItableviewautomaticDimension
Сэнди
источник