UITableViewCell з висотою UITextView в iOS 7?


121

Як я можу обчислити висоту UITableViewCell з UITextView в ньому в iOS 7?

Я знайшов багато відповідей на подібні питання, але sizeWithFont:бере участь у кожному рішенні, і цей метод застарів!

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

Відповіді:


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

Ця функція прийме а NSAttributedStringі потрібну ширину як a CGFloatі поверне потрібну висоту


Детальне рішення

Оскільки я нещодавно робив щось подібне, я думав, що також поділюсь деякими рішеннями пов'язаних з нами проблем. Сподіваюся, це комусь допоможе.

Це набагато глибше і охоплює наступне:

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

Якщо ви працюєте зі статичним поданням таблиці або у вас є лише відома кількість UITextView s, потенційно ви можете зробити крок 2 набагато простішим.

1. Спочатку перезапишіть висотуForRowAtIndexPath:

- (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 of 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пристрою можуть бути охоплені клавіатурою. Якщо вставки таблиці перегляду не скориговані, scrollToCursorForTextView:не вдасться прокрутити курсор, якщо він знаходиться в нижній частині таблиці.

- (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:все ще містив пряме посилання на одне з TextFields у своєму проекті. Якщо у вас є проблема з тим, що його bodyTextViewне знайшли, перевірте оновлену версію функції.


1
Цей код взагалі добре працює! Це зміни розміру все! Але мій TextView завжди отримує висоту 30 пікселів! Чи є налаштування, які мені заборонено встановлювати, чи є щось, що мені заборонено в UITextView?
MyJBMe

1
Це рішення, здається, не працює над копією та вставкою, якщо текст великий, якісь ідеї?
Вікінги

2
@Tim Bodeit, ваше рішення працює, дякую! Але я вважаю, що вам слід зауважити, що призначення attributedTextбез визначення шрифту, кольору та вирівнювання тексту призводить до встановлення значень атрибутів NSAttributedString за замовчуванням до textView. У моєму випадку це спричиняє отримання різної висоти перегляду тексту для одного і того ж тексту.
Олександр

4
Це одна з моїх улюблених за весь час відповідей Stack Overflow - дякую!
Річард Венебл

3
@TimBodeit: Я не в змозі змусити це працювати на iOS8. Будь ласка, дайте мені знати, як це можна виправити.
Арун Гупта

37

Існує нова функція заміни sizeWithFont, яка обмежуєRectWithSize.

Я додав у свій проект наступну функцію, яка використовує нову функцію на 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 )

Мої UITextViews все ще не дуже масштабуються і стають прокручуватися, коли текст охоплює 3 рядки; pastebin.com/Wh6vmBqh
Мартін де Кейзер

Другий оператор повернення також кидає попередження про депресію в XCode.
Мартін де Кейзер

Ви також встановлюєте розмір UItextView до обчисленого розміру тексту у комірціForRowAtIndexPath? Крім того, ви не повинні турбуватися про попередження у другому поверненні, оскільки воно використовується лише тоді, коли додаток запускається на пристрої iOS6, у якому функція не застаріла.
манекоста

Чи можете ви навести простий приклад того, як використовувати цю функцію?
Зорайр

Документація @manecosta Apple говорить про те, що ви повинні «перекрити» результат: в iOS 7 та пізніших версіях цей метод повертає дробові розміри (у компонент розміру повернутого CGRect); щоб використовувати повернення розміру, що повертається до розміру, потрібно використовувати підняти його значення до найближчого більшого цілого числа за допомогою функції ceil.
HpTerm

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 поля навколо всіх сторін подання тексту та заздалегідь визначене обмеження висоти для перегляду тексту.

1
перегляд тексту НЕ повинен прокручуватися
Akshit Zaveri

Під час updateTextviewHeight я отримую однаковий розмір весь час. Схоже, розмір вмісту неправильний. Прокрутка відключена.
Дволе

5

Відповідь Тіма Бодейта чудова. Я використовував код простого рішення, щоб правильно отримати висоту перегляду тексту, і використовувати цю висоту в heightForRowAtIndexPath. Але я не використовую решту відповідей, щоб змінити розмір перегляду тексту. Натомість я пишу код, щоб змінити frameперегляд тексту в cellForRowAtIndexPath.

Усе працює в iOS 6 і нижче, але в iOS 7 текст у вигляді перегляду тексту не може бути показаний повністю, навіть якщо frameперегляд тексту дійсно змінено. (Я не використовую Auto Layout). Це повинно бути причиною того, що в iOS 7 є, TextKitа положення тексту контролюється NSTextContainerв UITextView. Тому в моєму випадку мені потрібно додати рядок, щоб встановити параметр someTextViewдля того, щоб він працював правильно в iOS 7.

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

Як зазначено в документації, це майно:

Контролює, чи приймач регулює висоту обмежувального прямокутника, коли його розмір тексту зменшиться. Значення за замовчуванням: НІ.

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

І, можливо, це потрібно встановити на scrollEnabled = NOвипадок, якщо їх є більше textContainer, щоб текст переповнювався з одного textContainerна інший.


4

Ось ще одне рішення, яке спрямоване на простоту та швидке складання прототипів :

Налаштування:

  1. Таблиця з прототипами комірок.
  2. Кожна комірка містить динамічні розміри UITextView w / інший вміст.
  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.


Для чого означає цей "dream.dream"?
MyJBMe

@MyJBМе шкода, що це було частиною мого власного проекту - я відповідно оновив код. dream.dreamце був текст, який я передавав у текстовому вікні.
Зорайр

3

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

Я відповів на подібне запитання з повним зразком щодо розміру осередків tableview за допомогою автоматичного макета тут:

Як змінити розмір нагляду, щоб відповідати всім підглядам з автоматичним розкладом?


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 (щоб протистояти мінімізації висоти за допомогою автоматичного розкладу за допомогою UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: з тим же indexPath запускає табличний перегляд висоти та переглядання висоти tableViewCell.

  • performWithoutAnimation: видаляє побічний ефект (зміщення вмісту tableView, перехід на новий рядок під час введення тексту).

  • Важливо зберегти relativeFrameOriginY(не contentOffsetY!) Під час оновлення комірок, оскільки contentSizeкомірки перед тим, як поточну комірку можна було несподівано змінити методом автоматичного компонування. Він усуває візуальні стрибки на переносі системи під час введення довгих слів.

  • Зверніть увагу, що ви не повинні встановлювати властивість 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

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;

я думаю, що таким чином ви можете підрахувати висоту перегляду тексту, а потім змінити розмір комірки таблиці, відповідно до цієї висоти, щоб ви могли показати повний текст у комірці


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 у виглядDidLoad () встановіть таке tableView.rowHeight = UITableViewAutomaticDimensionі tableView.estimatedRowHeight > 0таке, як:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

Це воно. UITableViewCellВисота буде автоматично регулюватися залежно від внутрішньої UITextViewвисоти.


-2

Для iOS 8 і вище ви можете просто використовувати

your_tablview.estimatedrowheight= minheight ти хочеш

your_tableview.rowheight=UItableviewautomaticDimension
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.