在ios7中UITextView高度的UITableViewCell ?

时间:2022-11-13 10:11:07

How can I calculate the height of an UITableViewCell with an UITextView in it in iOS 7?

在ios7中如何计算UITableViewCell的高度?

I found a lot of answers on similar questions, but sizeWithFont: takes part in every solution and this method is deprecated!

我在类似的问题上找到了很多答案,但是sizeWithFont:参与到每个解决方案中,这个方法是不赞成的!

I know I have to use - (CGFloat)tableView:heightForRowAtIndexPath: but how do I calculate the height my TextView needs to display the whole text?

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

12 个解决方案

#1


422  

First of all, it is very important to note, that there is a big difference between UITextView and UILabel when it comes to how text is rendered. Not only does UITextView have insets on all borders, but also the text layout inside it is slightly different.

首先,值得注意的是,在如何呈现文本时,UITextView和UILabel之间有很大的差别。UITextView不仅在所有边框上都设置了insets,而且内部的文本布局也略有不同。

Therefore, sizeWithFont: is a bad way to go for UITextViews.

因此,sizeWithFont:对于UITextViews来说,这是一个糟糕的方法。

Instead UITextView itself has a function called sizeThatFits: which will return the smallest size needed to display all contents of the UITextView inside a bounding box, that you can specify.

相反,UITextView本身有一个名为sizethatfit的函数:它将返回显示在一个边框内的UITextView所有内容所需的最小大小,您可以指定。

The following will work equally for both iOS 7 and older versions and as of right now does not include any methods, that are deprecated.

以下将对iOS 7和旧版本进行同样的操作,目前还不包括不推荐使用的任何方法。


Simple Solution

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

This function will take a NSAttributedString and the desired width as a CGFloat and return the height needed

该函数将使用NSAttributedString和所需的宽度作为CGFloat,并返回所需的高度。


Detailed Solution

Since I have recently done something similar, I thought I would also share some solutions to the connected Issues I encountered. I hope it will help somebody.

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

This is far more in depth and will cover the following:

这要深入得多,并将涵盖以下内容:

  • Of course: setting the height of a UITableViewCell based on the size needed to display the full contents of a contained UITextView
  • 当然:根据显示包含的UITextView的全部内容所需的大小设置UITableViewCell的高度。
  • Respond to text changes (and animate the height changes of the row)
  • 响应文本更改(并激活行的高度更改)
  • Keeping the cursor inside the visible area and keeping first responder on the UITextView when resizing the UITableViewCell while editing
  • 在编辑时,将光标放在可见区域内,并在UITextView上保留第一个响应器,以调整UITableViewCell的大小。

If you are working with a static table view or you only have a known number of UITextViews, you can potentially make step 2 much simpler.

如果您使用的是静态表视图,或者您只有已知的uitextview的数量,那么您可以使步骤2变得更简单。

1. First, overwrite the 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. Define the function that calculated the needed height:

Add an NSMutableDictionary (in this example called textViews) as an instance variable to your UITableViewController subclass.

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

Use this dictionary to store references to the individual UITextViews like so:

使用此字典来存储对单个UITextViews的引用,例如:

(and yes, indexPaths are valid keys for dictionaries)

(是的,索引路径是字典的有效键)

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

This function will now calculate the actual height:

这个函数将计算实际的高度:

- (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. Enable Resizing while Editing

For the next two functions, it is important, that the delegate of the UITextViews is set to your UITableViewController. If you need something else as the delegate, you can work around it by making the relevant calls from there or using the appropriate NSNotificationCenter hooks.

对于接下来的两个函数,很重要的是,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. Follow cursor while Editing

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

This will make the UITableView scroll to the position of the cursor, if it is not inside the visible Rect of the UITableView:

这将使UITableView滚动到光标的位置,如果它不在UITableView的可视Rect内:

- (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. Adjust visible rect, by setting insets

While editing, parts of your UITableView may be covered by the Keyboard. If the tableviews insets are not adjusted, scrollToCursorForTextView: will not be able to scroll to your cursor, if it is at the bottom of the tableview.

编辑时,你的UITableView的部分可以被键盘覆盖。如果tableview insets没有被调整,那么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];
}

And last part:

最后的部分:

Inside your view did load, sign up for the Notifications for Keyboard changes through NSNotificationCenter:

在你的视图中有负载,通过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];
}

Please don't get mad at me, for making this answer so long. While not all of it is needed to answer the question, I believe that there are other people who these directly related issues will be helpful to.


UPDATE:

As Dave Haupert pointed out, I forgot to include the rectVisible function:

正如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);
}

Also I noticed, that scrollToCursorForTextView: still included a direct reference to one of the TextFields in my project. If you have a problem with bodyTextView not being found, check the updated version of the function.

我还注意到,scrollToCursorForTextView:仍然包含了我的项目中的一个文本字段的直接引用。如果您的bodyTextView没有找到问题,请检查该函数的更新版本。

#2


37  

There is a new function to replace sizeWithFont, which is boundingRectWithSize.

有一个新的函数来替换sizeWithFont,它是boundingRectWithSize。

I added the following function to my project, which makes use of the new function on iOS7 and the old one on iOS lower than 7. It has basically the same syntax as sizeWithFont:

我将下面的函数添加到我的项目中,它利用了iOS7上的新函数和iOS7上的旧函数。它的语法与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
        }
    }

You can add that IOS_NEWER_OR_EQUAL_TO_7 on your prefix.pch file in your project as:

您可以在前缀上添加IOS_NEWER_OR_EQUAL_TO_7。项目中的pch文件为:

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

#3


9  

If you're using UITableViewAutomaticDimension I have a really simple (iOS 8 only) solution. In my case it's a static table view, but i guess you could adapt this for dynamic prototypes...

如果你使用UITableViewAutomaticDimension,我有一个非常简单的(iOS 8)解决方案。在我的例子中,它是一个静态表格视图,但是我想您可以将其用于动态原型…

I have a constraint outlet for the text-view's height and I have implemented the following methods like this:

对于文本视图的高度,我有一个约束outlet,我已经实现如下方法:

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

But remember: the text view must be scrollable, and you must setup your constraints such that they work for automatic dimension:

但是请记住:文本视图必须是可滚动的,并且您必须设置您的约束,以便它们在自动维度中工作:

  • setup all the view in the cell in relation to each other, with fixed heights (including the text view height, which you will change programatically)
  • 在单元格中设置所有视图之间的关系,具有固定的高度(包括文本视图高度,您将按程序更改它)
  • the top most view has the top spacing and the bottom most view has the bottom spacing to the super view;
  • 最上面的视图有顶部的间距,底部的大部分视图有底部的间距到超级视图;

The most basic cell example is:

最基本的细胞例子是:

  • no other views in the cell except the textview
  • 除了textview外,单元格中没有其他视图。
  • 0 margins around all sides of the text view and a predefined height constraint for the text view.
  • 文本视图的所有边的边距和文本视图的预定义高度约束。

#4


5  

Tim Bodeit's answer is great. I used the code of Simple Solution to correctly get the height of the text view, and use that height in heightForRowAtIndexPath. But I don't use the rest of the answer to resize the text view. Instead, I write code to change the frame of text view in cellForRowAtIndexPath.

Tim Bodeit的回答很好。我使用简单解决方案的代码来正确地获取文本视图的高度,并在heightForRowAtIndexPath中使用这个高度。但是我不使用其他的答案来调整文本视图的大小。相反,我编写代码来改变cellForRowAtIndexPath中的文本视图框架。

Everything is working in iOS 6 and below, but in iOS 7 the text in text view cannot be fully shown even though the frame of text view is indeed resized. (I'm not using Auto Layout). It should be the reason that in iOS 7 there's TextKit and the position of the text is controlled by NSTextContainer in UITextView. So in my case I need to add a line to set the someTextView in order to make it work correctly in iOS 7.

所有的东西都在ios6和下面,但是在ios7中文本视图的文本不能完全显示,即使文本视图的框架确实被调整了大小。(我没有使用自动布局)。应该是在ios7中,TextKit和文本的位置是由UITextView中的NSTextContainer控制的。在我的例子中,我需要添加一行来设置someTextView以便使它在ios7中正确工作。

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

As the documentation said, what that property does is:

正如文件所说,该财产的作用是:

Controls whether the receiver adjusts the height of its bounding rectangle when its text view is resized. Default value: NO.

控制当文本视图被调整时,接收器是否调整其边界矩形的高度。默认值:没有。

If leave it with the default value, after resize the frame of someTextView, the size of the textContainer is not changed, leading to the result that the text can only be displayed in the area before resizing.

如果将其保留为默认值,在调整了someTextView的框架后,textContainer的大小不会改变,导致文本只能在调整大小之前显示在该区域中。

And maybe it is needed to set the scrollEnabled = NO in case there's more than one textContainer, so that the text will reflow from one textContainer to the another.

可能需要设置scrollEnabled = NO,以防有多个textContainer,这样文本就会从一个文本容器回流到另一个文本容器。

#5


4  

Here is one more solution that aims at simplicity and quick prototyping:

这里还有一个旨在简化和快速原型化的解决方案:

Setup:

设置:

  1. Table with prototype cells.
  2. 表与原型细胞。
  3. Each cell contains dynamic sized UITextView w/ other contents.
  4. 每个单元格包含动态大小的UITextView w/其他内容。
  5. Prototype cells are associated with TableCell.h.
  6. 原型细胞与TableCell.h有关。
  7. UITableView is associated with TableViewController.h.
  8. UITableView与tableviewcontroller。h相关。

Solution:

解决方案:

(1) Add to TableViewController.m:

(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) Add to TableCell.m:

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

Explanation:

解释:

So what's happening here is this: each text view is bound to the height of the table cells by vertical and horizontal constraints - that means when the table cell height increases, the text view increases its size as well. I used a modified version of @manecosta's code to calculate the required height of a text view to fit the given text in a cell. So that means given a text with X number of characters, frameForText: will return a size which will have a property size.height that matches the text view's required height.

这里发生的情况是:每个文本视图都被垂直和水平约束绑定到表单元的高度——这意味着当表单元格高度增加时,文本视图也会增加它的大小。我使用了修改后的@manecosta代码来计算文本视图的所需高度,以适应单元格中给定的文本。因此,这意味着给定一个具有X个字符的文本,frameForText:将返回一个具有属性大小的大小。与文本视图所需高度匹配的高度。

Now, all that remains is the update the cell's height to match the required text view's height. And this is achieved at heightForRowAtIndexPath:. As noted in the comments, since size.height is only the height for the text view and not the entire cell, there should be some offset added to it. In the case of the example, this value was 80.

现在,剩下的就是更新单元格的高度,以匹配所需的文本视图的高度。这是在heightForRowAtIndexPath:。正如评论中提到的,因为尺寸。高度只是文本视图的高度,而不是整个单元格,应该有一些偏移。在例子中,这个值是80。

#6


3  

One approach if you're using autolayout is to let the autolayout engine calculate the size for you. This isn't the most efficient approach but it is pretty convenient (and arguably the most accurate). It becomes more convenient as the complexity of the cell layout grows - e.g. suddenly you have two or more textviews/fields in the cell.

如果你使用autolayout,一种方法是让autolayout引擎计算你的尺寸。这不是最有效的方法,但它非常方便(而且可以说是最精确的)。随着单元布局的复杂性增加,它变得更加方便——例如,在单元格中突然有两个或多个textview /字段。

I answered a similar question with a complete sample for sizing tableview cells using auto layout, here:

我回答了一个类似的问题,有一个完整的样本,用于使用自动布局的大小tableview单元,这里:

How to resize superview to fit all subviews with autolayout?

如何调整超级视图以适应所有的子视图与自动布局?

#7


1  

The complete smooth solution is as follows.

完整的光滑解如下。

First, we need the cell class with a textView

首先,我们需要使用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];
    }
}

Next, we use it in the TableViewController

接下来,我们在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).

    在这里,minLines允许为textView设置最小高度(通过使用UITableViewAutomaticDimension来抵御高度最小化)。

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

    indexPath:使用相同的indexPath启动tableViewCell高度重新计算和重新布局。

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

    performWithoutAnimation:移除副作用(tableView的内容偏移在键入时开始新行)。

  • 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

    注意,您不应该设置属性估计rowh8 !以下是行不通的

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    

    Use only tableViewDelegate method.

    只使用tableViewDelegate方法。

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

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

If one doesn't mind against weak binding between tableView and tableViewCell and updating geometry of the tableView from tableViewCell, it is possible to upgrade TextInputTableViewCell class above:

如果一个人不反对tableView和tableViewCell之间的弱绑定,并从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

#8


1  

  1. Put UILabel behind your UITextView.
  2. 把UILabel放在UITextView后面。
  3. Use this answer: https://*.com/a/36054679/6681462 to UILabel you created
  4. 使用这个答案:https://*.com/a/36054679/6681462到你创建的UILabel。
  5. Give them same constraints and fonts
  6. 给他们同样的限制和字体。
  7. Set them same text;
  8. 让他们同样的文本;

Your cell's height will calculate by UILabel's content, but all text will be showed by TextField.

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

#9


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;

i think this way you can count the height of your text view and then resize your tableview cell according to that height so that you can show full text on cell

我认为这样可以计算文本视图的高度,然后根据高度调整tableview单元格,这样就可以在单元格上显示全文了。

#10


0  

Swift version

斯威夫特版本

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
}

#11


0  

If you want to automatically adjust UITableViewCell's height based on the height of the inner UITextView's height. See my answer here: https://*.com/a/45890087/1245231

如果您想根据内UITextView的高度来自动调整UITableViewCell的高度。查看我的答案:https://*.com/a/45890087/1245231。

The solution is quite simple and should work since iOS 7. Make sure that the Scrolling Enabled option is turned off for the UITextView inside the UITableViewCell in the StoryBoard.

解决方案非常简单,应该在ios7之后工作。确保在故事板中UITableViewCell内的UITextView关闭了启用滚动的选项。

Then in your UITableViewController's viewDidLoad() set the tableView.rowHeight = UITableViewAutomaticDimension and tableView.estimatedRowHeight > 0 such as:

然后在UITableViewController的viewDidLoad()设置tableView。rowh8 = UITableViewAutomaticDimension和tableview . estimatedrowh8 >,如:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

That's it. UITableViewCell's height will be automatically adjusted based on the inner UITextView's height.

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

#12


-2  

For iOS 8 and above you can just use

对于iOS 8和以上你可以使用。

your_tablview.estimatedrowheight= minheight you want

your_tablview.estimatedrowheight = minheight你想要的

your_tableview.rowheight=UItableviewautomaticDimension

#1


422  

First of all, it is very important to note, that there is a big difference between UITextView and UILabel when it comes to how text is rendered. Not only does UITextView have insets on all borders, but also the text layout inside it is slightly different.

首先,值得注意的是,在如何呈现文本时,UITextView和UILabel之间有很大的差别。UITextView不仅在所有边框上都设置了insets,而且内部的文本布局也略有不同。

Therefore, sizeWithFont: is a bad way to go for UITextViews.

因此,sizeWithFont:对于UITextViews来说,这是一个糟糕的方法。

Instead UITextView itself has a function called sizeThatFits: which will return the smallest size needed to display all contents of the UITextView inside a bounding box, that you can specify.

相反,UITextView本身有一个名为sizethatfit的函数:它将返回显示在一个边框内的UITextView所有内容所需的最小大小,您可以指定。

The following will work equally for both iOS 7 and older versions and as of right now does not include any methods, that are deprecated.

以下将对iOS 7和旧版本进行同样的操作,目前还不包括不推荐使用的任何方法。


Simple Solution

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

This function will take a NSAttributedString and the desired width as a CGFloat and return the height needed

该函数将使用NSAttributedString和所需的宽度作为CGFloat,并返回所需的高度。


Detailed Solution

Since I have recently done something similar, I thought I would also share some solutions to the connected Issues I encountered. I hope it will help somebody.

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

This is far more in depth and will cover the following:

这要深入得多,并将涵盖以下内容:

  • Of course: setting the height of a UITableViewCell based on the size needed to display the full contents of a contained UITextView
  • 当然:根据显示包含的UITextView的全部内容所需的大小设置UITableViewCell的高度。
  • Respond to text changes (and animate the height changes of the row)
  • 响应文本更改(并激活行的高度更改)
  • Keeping the cursor inside the visible area and keeping first responder on the UITextView when resizing the UITableViewCell while editing
  • 在编辑时,将光标放在可见区域内,并在UITextView上保留第一个响应器,以调整UITableViewCell的大小。

If you are working with a static table view or you only have a known number of UITextViews, you can potentially make step 2 much simpler.

如果您使用的是静态表视图,或者您只有已知的uitextview的数量,那么您可以使步骤2变得更简单。

1. First, overwrite the 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. Define the function that calculated the needed height:

Add an NSMutableDictionary (in this example called textViews) as an instance variable to your UITableViewController subclass.

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

Use this dictionary to store references to the individual UITextViews like so:

使用此字典来存储对单个UITextViews的引用,例如:

(and yes, indexPaths are valid keys for dictionaries)

(是的,索引路径是字典的有效键)

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

This function will now calculate the actual height:

这个函数将计算实际的高度:

- (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. Enable Resizing while Editing

For the next two functions, it is important, that the delegate of the UITextViews is set to your UITableViewController. If you need something else as the delegate, you can work around it by making the relevant calls from there or using the appropriate NSNotificationCenter hooks.

对于接下来的两个函数,很重要的是,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. Follow cursor while Editing

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

This will make the UITableView scroll to the position of the cursor, if it is not inside the visible Rect of the UITableView:

这将使UITableView滚动到光标的位置,如果它不在UITableView的可视Rect内:

- (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. Adjust visible rect, by setting insets

While editing, parts of your UITableView may be covered by the Keyboard. If the tableviews insets are not adjusted, scrollToCursorForTextView: will not be able to scroll to your cursor, if it is at the bottom of the tableview.

编辑时,你的UITableView的部分可以被键盘覆盖。如果tableview insets没有被调整,那么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];
}

And last part:

最后的部分:

Inside your view did load, sign up for the Notifications for Keyboard changes through NSNotificationCenter:

在你的视图中有负载,通过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];
}

Please don't get mad at me, for making this answer so long. While not all of it is needed to answer the question, I believe that there are other people who these directly related issues will be helpful to.


UPDATE:

As Dave Haupert pointed out, I forgot to include the rectVisible function:

正如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);
}

Also I noticed, that scrollToCursorForTextView: still included a direct reference to one of the TextFields in my project. If you have a problem with bodyTextView not being found, check the updated version of the function.

我还注意到,scrollToCursorForTextView:仍然包含了我的项目中的一个文本字段的直接引用。如果您的bodyTextView没有找到问题,请检查该函数的更新版本。

#2


37  

There is a new function to replace sizeWithFont, which is boundingRectWithSize.

有一个新的函数来替换sizeWithFont,它是boundingRectWithSize。

I added the following function to my project, which makes use of the new function on iOS7 and the old one on iOS lower than 7. It has basically the same syntax as sizeWithFont:

我将下面的函数添加到我的项目中,它利用了iOS7上的新函数和iOS7上的旧函数。它的语法与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
        }
    }

You can add that IOS_NEWER_OR_EQUAL_TO_7 on your prefix.pch file in your project as:

您可以在前缀上添加IOS_NEWER_OR_EQUAL_TO_7。项目中的pch文件为:

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

#3


9  

If you're using UITableViewAutomaticDimension I have a really simple (iOS 8 only) solution. In my case it's a static table view, but i guess you could adapt this for dynamic prototypes...

如果你使用UITableViewAutomaticDimension,我有一个非常简单的(iOS 8)解决方案。在我的例子中,它是一个静态表格视图,但是我想您可以将其用于动态原型…

I have a constraint outlet for the text-view's height and I have implemented the following methods like this:

对于文本视图的高度,我有一个约束outlet,我已经实现如下方法:

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

But remember: the text view must be scrollable, and you must setup your constraints such that they work for automatic dimension:

但是请记住:文本视图必须是可滚动的,并且您必须设置您的约束,以便它们在自动维度中工作:

  • setup all the view in the cell in relation to each other, with fixed heights (including the text view height, which you will change programatically)
  • 在单元格中设置所有视图之间的关系,具有固定的高度(包括文本视图高度,您将按程序更改它)
  • the top most view has the top spacing and the bottom most view has the bottom spacing to the super view;
  • 最上面的视图有顶部的间距,底部的大部分视图有底部的间距到超级视图;

The most basic cell example is:

最基本的细胞例子是:

  • no other views in the cell except the textview
  • 除了textview外,单元格中没有其他视图。
  • 0 margins around all sides of the text view and a predefined height constraint for the text view.
  • 文本视图的所有边的边距和文本视图的预定义高度约束。

#4


5  

Tim Bodeit's answer is great. I used the code of Simple Solution to correctly get the height of the text view, and use that height in heightForRowAtIndexPath. But I don't use the rest of the answer to resize the text view. Instead, I write code to change the frame of text view in cellForRowAtIndexPath.

Tim Bodeit的回答很好。我使用简单解决方案的代码来正确地获取文本视图的高度,并在heightForRowAtIndexPath中使用这个高度。但是我不使用其他的答案来调整文本视图的大小。相反,我编写代码来改变cellForRowAtIndexPath中的文本视图框架。

Everything is working in iOS 6 and below, but in iOS 7 the text in text view cannot be fully shown even though the frame of text view is indeed resized. (I'm not using Auto Layout). It should be the reason that in iOS 7 there's TextKit and the position of the text is controlled by NSTextContainer in UITextView. So in my case I need to add a line to set the someTextView in order to make it work correctly in iOS 7.

所有的东西都在ios6和下面,但是在ios7中文本视图的文本不能完全显示,即使文本视图的框架确实被调整了大小。(我没有使用自动布局)。应该是在ios7中,TextKit和文本的位置是由UITextView中的NSTextContainer控制的。在我的例子中,我需要添加一行来设置someTextView以便使它在ios7中正确工作。

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

As the documentation said, what that property does is:

正如文件所说,该财产的作用是:

Controls whether the receiver adjusts the height of its bounding rectangle when its text view is resized. Default value: NO.

控制当文本视图被调整时,接收器是否调整其边界矩形的高度。默认值:没有。

If leave it with the default value, after resize the frame of someTextView, the size of the textContainer is not changed, leading to the result that the text can only be displayed in the area before resizing.

如果将其保留为默认值,在调整了someTextView的框架后,textContainer的大小不会改变,导致文本只能在调整大小之前显示在该区域中。

And maybe it is needed to set the scrollEnabled = NO in case there's more than one textContainer, so that the text will reflow from one textContainer to the another.

可能需要设置scrollEnabled = NO,以防有多个textContainer,这样文本就会从一个文本容器回流到另一个文本容器。

#5


4  

Here is one more solution that aims at simplicity and quick prototyping:

这里还有一个旨在简化和快速原型化的解决方案:

Setup:

设置:

  1. Table with prototype cells.
  2. 表与原型细胞。
  3. Each cell contains dynamic sized UITextView w/ other contents.
  4. 每个单元格包含动态大小的UITextView w/其他内容。
  5. Prototype cells are associated with TableCell.h.
  6. 原型细胞与TableCell.h有关。
  7. UITableView is associated with TableViewController.h.
  8. UITableView与tableviewcontroller。h相关。

Solution:

解决方案:

(1) Add to TableViewController.m:

(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) Add to TableCell.m:

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

Explanation:

解释:

So what's happening here is this: each text view is bound to the height of the table cells by vertical and horizontal constraints - that means when the table cell height increases, the text view increases its size as well. I used a modified version of @manecosta's code to calculate the required height of a text view to fit the given text in a cell. So that means given a text with X number of characters, frameForText: will return a size which will have a property size.height that matches the text view's required height.

这里发生的情况是:每个文本视图都被垂直和水平约束绑定到表单元的高度——这意味着当表单元格高度增加时,文本视图也会增加它的大小。我使用了修改后的@manecosta代码来计算文本视图的所需高度,以适应单元格中给定的文本。因此,这意味着给定一个具有X个字符的文本,frameForText:将返回一个具有属性大小的大小。与文本视图所需高度匹配的高度。

Now, all that remains is the update the cell's height to match the required text view's height. And this is achieved at heightForRowAtIndexPath:. As noted in the comments, since size.height is only the height for the text view and not the entire cell, there should be some offset added to it. In the case of the example, this value was 80.

现在,剩下的就是更新单元格的高度,以匹配所需的文本视图的高度。这是在heightForRowAtIndexPath:。正如评论中提到的,因为尺寸。高度只是文本视图的高度,而不是整个单元格,应该有一些偏移。在例子中,这个值是80。

#6


3  

One approach if you're using autolayout is to let the autolayout engine calculate the size for you. This isn't the most efficient approach but it is pretty convenient (and arguably the most accurate). It becomes more convenient as the complexity of the cell layout grows - e.g. suddenly you have two or more textviews/fields in the cell.

如果你使用autolayout,一种方法是让autolayout引擎计算你的尺寸。这不是最有效的方法,但它非常方便(而且可以说是最精确的)。随着单元布局的复杂性增加,它变得更加方便——例如,在单元格中突然有两个或多个textview /字段。

I answered a similar question with a complete sample for sizing tableview cells using auto layout, here:

我回答了一个类似的问题,有一个完整的样本,用于使用自动布局的大小tableview单元,这里:

How to resize superview to fit all subviews with autolayout?

如何调整超级视图以适应所有的子视图与自动布局?

#7


1  

The complete smooth solution is as follows.

完整的光滑解如下。

First, we need the cell class with a textView

首先,我们需要使用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];
    }
}

Next, we use it in the TableViewController

接下来,我们在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).

    在这里,minLines允许为textView设置最小高度(通过使用UITableViewAutomaticDimension来抵御高度最小化)。

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

    indexPath:使用相同的indexPath启动tableViewCell高度重新计算和重新布局。

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

    performWithoutAnimation:移除副作用(tableView的内容偏移在键入时开始新行)。

  • 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

    注意,您不应该设置属性估计rowh8 !以下是行不通的

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    

    Use only tableViewDelegate method.

    只使用tableViewDelegate方法。

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

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

If one doesn't mind against weak binding between tableView and tableViewCell and updating geometry of the tableView from tableViewCell, it is possible to upgrade TextInputTableViewCell class above:

如果一个人不反对tableView和tableViewCell之间的弱绑定,并从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

#8


1  

  1. Put UILabel behind your UITextView.
  2. 把UILabel放在UITextView后面。
  3. Use this answer: https://*.com/a/36054679/6681462 to UILabel you created
  4. 使用这个答案:https://*.com/a/36054679/6681462到你创建的UILabel。
  5. Give them same constraints and fonts
  6. 给他们同样的限制和字体。
  7. Set them same text;
  8. 让他们同样的文本;

Your cell's height will calculate by UILabel's content, but all text will be showed by TextField.

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

#9


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;

i think this way you can count the height of your text view and then resize your tableview cell according to that height so that you can show full text on cell

我认为这样可以计算文本视图的高度,然后根据高度调整tableview单元格,这样就可以在单元格上显示全文了。

#10


0  

Swift version

斯威夫特版本

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
}

#11


0  

If you want to automatically adjust UITableViewCell's height based on the height of the inner UITextView's height. See my answer here: https://*.com/a/45890087/1245231

如果您想根据内UITextView的高度来自动调整UITableViewCell的高度。查看我的答案:https://*.com/a/45890087/1245231。

The solution is quite simple and should work since iOS 7. Make sure that the Scrolling Enabled option is turned off for the UITextView inside the UITableViewCell in the StoryBoard.

解决方案非常简单,应该在ios7之后工作。确保在故事板中UITableViewCell内的UITextView关闭了启用滚动的选项。

Then in your UITableViewController's viewDidLoad() set the tableView.rowHeight = UITableViewAutomaticDimension and tableView.estimatedRowHeight > 0 such as:

然后在UITableViewController的viewDidLoad()设置tableView。rowh8 = UITableViewAutomaticDimension和tableview . estimatedrowh8 >,如:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

That's it. UITableViewCell's height will be automatically adjusted based on the inner UITextView's height.

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

#12


-2  

For iOS 8 and above you can just use

对于iOS 8和以上你可以使用。

your_tablview.estimatedrowheight= minheight you want

your_tablview.estimatedrowheight = minheight你想要的

your_tableview.rowheight=UItableviewautomaticDimension