在iOS 7中具有UITextView高度的UITableViewCell?

MyJ*_*BMe 121 height cocoa-touch uitableview ios7

如何在iOS 7中使用UITextView计算UITableViewCell的高度?

我在类似的问题上找到了很多答案,但sizeWithFont:参与了每个解决方案,这个方法已被弃用!

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

Tim*_*eit 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;
}
Run Code Online (Sandbox Code Playgroud)

此功能将a NSAttributedString和所需的宽度作为a CGFloat并返回所需的高度


详细解决方案

由于我最近做了类似的事情,我想我也会分享一些我遇到的连接问题的解决方案.我希望它会对某人有所帮助.

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

  • 当然:UITableViewCell根据显示包含的全部内容所需的大小来设置a的高度UITextView
  • 响应文本更改(并为行的高度更改设置动画)
  • 保持光标可见区域内,并保持在第一响应UITextView调整的时候UITableViewCell,同时编辑

如果您正在使用静态表视图或只有已知数量的UITextViews,则可以使步骤2更加简单.

1.首先,覆盖heightForRowAtIndexPath:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // check here, if it is one of the cells, that needs to be resized
    // to the size of the contained UITextView
    if (  )             
        return [self textViewHeightForRowAtIndexPath:indexPath];
    else
    // return your normal height here:
            return 100.0;           
}
Run Code Online (Sandbox Code Playgroud)

2.定义计算所需高度的函数:

NSMutableDictionarytextViews您的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;
}
Run Code Online (Sandbox Code Playgroud)

此功能现在将计算实际高度:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

4.编辑时按光标

- (void)textViewDidBeginEditing:(UITextView *)textView {
    [self scrollToCursorForTextView:textView];
}
Run Code Online (Sandbox Code Playgroud)

这将使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];
    }
}
Run Code Online (Sandbox Code Playgroud)

5.通过设置插入来调整可见矩形

编辑时,部分UITableView键盘可能会被覆盖.如果未调整tableviews 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];
}
Run Code Online (Sandbox Code Playgroud)

最后一部分:

您的视图内部确实已加载,请通过以下方式注册键盘更改通知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];
}
Run Code Online (Sandbox Code Playgroud)

因为这个答案很长,请不要生我的气.虽然并非所有这些都需要回答这个问题,但我相信还有其他人会对这些直接相关的问题有所帮助.


更新:

正如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);
}
Run Code Online (Sandbox Code Playgroud)

我还注意到,它scrollToCursorForTextView:仍然包含对我项目中的一个TextField的直接引用.如果您bodyTextView找不到问题,请检查该功能的更新版本.

  • 这是我最喜欢的Stack Overflow答案之一 - 谢谢! (4认同)
  • @TimBodeit:我无法在iOS8上使用它.请告诉我如何解决这个问题. (3认同)
  • @Tim Bodeit,您的解决方案有效,谢谢!但我认为你应该在评论中注意到,在没有指定字体,颜色和文本对齐的情况下分配`attributedText`会导致将NSAttributedString属性的默认值设置为textView.在我的情况下,它会导致同一文本的textview达到不同的高度. (2认同)

man*_*sta 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
        }
    }
Run Code Online (Sandbox Code Playgroud)

您可以在项目的prefix.pch文件中添加IOS_NEWER_OR_EQUAL_TO_7,如下所示:

#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )
Run Code Online (Sandbox Code Playgroud)


dec*_*100 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];
}
Run Code Online (Sandbox Code Playgroud)

但请记住:文本视图必须是可滚动的,您必须设置约束,使它们适用于自动维度:

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

最基本的单元格示例是:

  • 除textview之外,单元格中没有其他视图
  • 文本视图所有边的0边距和文本视图的预定义高度约束.


Pow*_*ian 5

Tim Bodeit的答案很棒.我使用Simple Solution的代码正确获取文本视图的高度,并使用该高度heightForRowAtIndexPath.但我不会使用其余的答案来调整文本视图的大小.相反,我编写代码来更改frame文本视图cellForRowAtIndexPath.

一切都在iOS 6及更低版本中运行,但在iOS 7中,即使frame文本视图确实已调整大小,也无法完全显示文本视图中的文本.(我没用Auto Layout).这应该是在iOS 7中存在的原因,TextKit并且文本的位置由NSTextContainerin 控制UITextView.所以在我的情况下,我需要添加一行来设置someTextView,以使其在iOS 7中正常工作.

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        someTextView.textContainer.heightTracksTextView = YES;
    }
Run Code Online (Sandbox Code Playgroud)

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

控制接收器在调整文本视图大小时是否调整其边界矩形的高度.默认值:NO.

如果使用默认值,离开它,之后调整framesomeTextView,的大小textContainer没有改变,导致的结果是,该文本只能调整之前显示在该地区.

也许需要设置scrollEnabled = NO万一以上textContainer,以便文本将从一个回流textContainer到另一个.