iOS 7中UITableViewCell和UITextView的高度问题?

121

如何在iOS 7中计算UITableViewCell中带有UITextView的高度?

我在类似的问题上找到了很多答案,但是每个解决方案都涉及到sizeWithFont:方法,而这个方法已经被弃用了!

我知道我必须使用- (CGFloat)tableView:heightForRowAtIndexPath:方法,但是如何计算我的TextView需要显示整个文本的高度呢?

12个回答

428

首先,需要注意的是,UITextView和UILabel在文本渲染方面有很大的区别。UITextView不仅具有所有边框的插图,而且其中的文本布局略有不同。

因此,使用sizeWithFont:方法来计算UITextView的尺寸是不好的。

相反,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和所需宽度作为CGFloat,并返回所需的高度。


详细解决方案

由于我最近做了类似的事情,我想分享一些我遇到的相关问题的解决方案。希望能对某些人有所帮助。

这是更深入的内容,将涵盖以下内容:

  • 当显示包含的UITextView的完整内容所需的大小时:设置UITableViewCell的高度
  • 响应文本更改(并动画显示行的高度更改)
  • 在编辑时调整UITableViewCell的大小时,保持光标在可见区域内,并保持第一个响应者位于UITextView

如果您正在使用静态表视图或仅具有已知数量的UITextView,则可以使步骤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. 定义计算所需高度的函数:

在您的UITableViewController子类中添加一个NSMutableDictionary(本示例中称为textViews)作为实例变量。

使用此字典来存储各个UITextView的引用,如下所示:

(是的,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. 启用编辑时调整大小

对于接下来的两个功能,重要的是将UITextView的代理设置为您的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可见的范围之内,这将使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];
}

请不要因为我的答案太长而生气。虽然并非所有内容都是回答问题所必需的,但我认为与这些直接相关的问题有关的其他信息可能对其他人也有帮助。


更新:

正如Dave Haupert指出的那样,我忘记包括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: 中仍包含直接引用我项目中的一个TextField的代码。如果您遇到bodyTextView未被找到的问题,请检查更新后的函数版本。


1
那段代码完全正常!它可以调整任何东西的大小!但是,我的TextView总是得到一个高度为30像素的值!我没有权限设置某些设置吗?或者在UITextView中有我不能使用的功能? - MyJBMe
1
这个解决方案似乎在复制和粘贴大段文本时无效,有什么想法吗? - Vikings
2
@Tim Bodeit,你的解决方案很好,谢谢!但我认为你应该在注释中指出,如果没有指定字体、颜色和文本对齐方式,直接分配attributedText会导致NSAttributedString属性的默认值被设置到textView上。在我的情况下,这会导致相同文本的textview高度不同。 - Alexander
4
这是我一直以来最喜欢的 Stack Overflow 答案之一 - 谢谢! - Richard Venable
3
@TimBodeit:我无法在iOS8上让这个工作。请告诉我如何解决。 - Arun Gupta
显示剩余31条评论

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 )

我的UITextViews仍然不能很好地缩放,当文本跨越3行时变得可滚动;http://pastebin.com/Wh6vmBqh - Martin de Keijzer
第二个返回语句在XCode中也会抛出弃用警告。 - Martin de Keijzer
你是否在cellForRowAtIndexPath方法中将UITextView的大小设置为文本的计算大小?其次,不必担心第二个返回值中的警告,因为它仅在应用程序在iOS6设备上运行且该函数已被废弃时使用。 - manecosta
你能提供一个简单的例子来展示如何使用这个函数吗? - Zorayr
@manecosta 苹果的文档指出,您必须使用 'ceil' 函数来处理结果:在 iOS 7 及更高版本中,此方法返回带有小数的尺寸(在返回的 CGRect 的 size 组件中);如果要使用返回的尺寸来调整视图大小,则必须使用 ceil 函数将其值提高到最近的整数。 - HpTerm
@MartindeKeijzer 答案已更新,添加了 #pragma 标记以禁用警告,因为正在进行手动的 iOS 版本检查。 - Wilmar

10

如果您正在使用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];
}

但要记住:文本视图必须可滚动,并且您必须设置约束以便它们适用于自动调整大小:

  • 将单元格中的所有视图相对彼此设置,使用固定高度(包括您将通过编程更改的文本视图高度)
  • 最上面的视图具有与超级视图的顶部间距,最下面的视图具有与超级视图的底部间距;

最基本的单元格示例为:

  • 除文本视图外,单元格中没有其他视图
  • 文本视图周围没有边距,并且对于文本视图存在预定义的高度约束。

1
文本视图必须不可滚动 - Akshit Zaveri
我一直在updateTextviewHeight下得到相同的大小。看起来内容大小是错误的。滚动被禁用了。 - Dvole

5

Tim Bodeit的回答很好。我使用了Simple Solution的代码来正确获取文本视图的高度,并在heightForRowAtIndexPath中使用该高度。但是,我没有使用其他部分来调整文本视图的大小。相反,我编写了代码来在cellForRowAtIndexPath中更改文本视图的frame。

在iOS 6及以下版本中,一切都正常,但在iOS 7中,即使文本视图的frame确实被调整,文本视图中的文本也无法完全显示。(我没有使用自动布局)。这应该是因为在iOS 7中有TextKit,而文本的位置由UITextView中的NSTextContainer控制。因此,在我的情况下,我需要添加一行代码来设置someTextView,以使其在iOS 7中正常工作。

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

根据文档所述,该属性的作用是:
控制接收器在其文本视图被调整大小时是否调整其边界矩形的高度。默认值为NO。
如果保留默认值,在调整someTextView的frame后,textContainer的大小不会改变,导致文本只能显示在调整前的区域内。
如果存在多个textContainer,则可能需要将scrollEnabled设置为NO,以便文本从一个textContainer重新流动到另一个textContainer。

4
这里有一个旨在实现简单和快速原型设计的解决方案:

设置:

  1. 包含原型单元格的表格。
  2. 每个单元格包含动态大小的UITextView和其他内容。
  3. 原型单元格与TableCell.h相关联。
  4. UITableViewTableViewController.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
@MyJBMe 很抱歉,那是我自己项目的一部分 - 我已经相应地更新了代码。dream.dream 是我在文本视图中呈现的文本。 - Zorayr

3
如果您使用自动布局,一种方法是让自动布局引擎为您计算大小。这不是最有效的方法,但它非常方便(并且可以说是最准确的)。随着单元格布局的复杂性增加,它变得更加方便 - 例如,突然间您在单元格中有两个或更多的文本视图/字段。
我曾用一个完整的示例回答了一个类似的问题,该示例使用自动布局调整表格视图单元格的大小,请参见: 如何使用自动布局调整父视图以适应所有子视图?

1
完整的平滑解决方案如下。
首先,我们需要一个带有textView的cell类。
@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
  • Here minLines allows to set minimum height for the textView (to resist height minimizing by AutoLayout with UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: with the same indexPath starts tableViewCell height re-calculation and re-layout.

  • performWithoutAnimation: removes side-effect (tableView content offset jumping on starting new line while typing).

  • It is important to preserve relativeFrameOriginY (not contentOffsetY!) during cell update because contentSize of the cells before the current cell could be change by autoLayout calculus in unexpected way. It removes visual jumps on system hyphenation while typing long words.

  • Note that you shouldn't set the property estimatedRowHeight! The following doesn't work

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    

    Use only tableViewDelegate method.

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

如果不介意tableViewtableViewCell之间的弱绑定以及从tableViewCell更新tableView的几何形状,那么可以升级上面的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://dev59.com/8FoV5IYBdhLWcg3wmPpH#36054679 将您创建的UILabel应用
  3. 给它们相同的约束和字体
  4. 将它们设置为相同的文本;

您单元格的高度将由UILabel的内容计算,但所有文本都将通过TextField显示。


0

如果你想根据内部UITextView的高度自动调整UITableViewCell的高度,请参考我在这里的答案:https://dev59.com/3G445IYBdhLWcg3wXZS9#45890087

解决方案非常简单,应该适用于iOS 7及以上版本。确保在StoryBoard中UITableViewCell内部的UITextViewScrolling Enabled选项已经关闭off

然后在你的UITableViewControllerviewDidLoad()方法中设置tableView.rowHeight = UITableViewAutomaticDimensiontableView.estimatedRowHeight > 0,如下所示:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

就是这样。 UITableViewCell 的高度将根据内部的 UITextView 的高度自动调整。


0

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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接