UITableViewCell với chiều cao UITextView trong iOS 7?


121

Làm cách nào để tính chiều cao của UITableViewCell với UITextView trong đó trong iOS 7?

Tôi đã tìm thấy rất nhiều câu trả lời cho các câu hỏi tương tự, nhưng sizeWithFont:tham gia vào mọi giải pháp và phương pháp này không được dùng nữa!

Tôi biết tôi phải sử dụng - (CGFloat)tableView:heightForRowAtIndexPath:nhưng làm cách nào để tính chiều cao mà TextView của tôi cần để hiển thị toàn bộ văn bản?

Câu trả lời:


428

Trước hết, điều rất quan trọng cần lưu ý là có sự khác biệt lớn giữa UITextView và UILabel khi nói đến cách văn bản được hiển thị. UITextView không chỉ có nội dung trên tất cả các đường viền mà cả bố cục văn bản bên trong nó cũng hơi khác một chút.

Do đó, đây sizeWithFont:là một cách tồi để sử dụng UITextViews.

Thay vào đó, UITextViewbản thân nó có một hàm được gọi là hàm sizeThatFits:này sẽ trả về kích thước nhỏ nhất cần thiết để hiển thị tất cả nội dung UITextViewbên trong hộp giới hạn mà bạn có thể chỉ định.

Các phương pháp sau sẽ hoạt động như nhau cho cả iOS 7 và các phiên bản cũ hơn và tính đến thời điểm hiện tại không bao gồm bất kỳ phương pháp nào không được dùng nữa.


Giải pháp đơn giản

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

Hàm này sẽ lấy a NSAttributedStringvà chiều rộng mong muốn là a CGFloatvà trả về chiều cao cần thiết


Giải pháp chi tiết

Vì gần đây tôi đã làm điều gì đó tương tự, tôi nghĩ tôi cũng sẽ chia sẻ một số giải pháp cho các Vấn đề được kết nối mà tôi gặp phải. Tôi hy vọng nó sẽ giúp ai đó.

Điều này chuyên sâu hơn nhiều và sẽ bao gồm những điều sau:

  • Tất nhiên: đặt chiều cao của a UITableViewCelldựa trên kích thước cần thiết để hiển thị đầy đủ nội dung của mộtUITextView
  • Phản hồi các thay đổi văn bản (và tạo hiệu ứng cho các thay đổi về độ cao của hàng)
  • Giữ con trỏ bên trong vùng hiển thị và giữ người trả lời đầu tiên ở chế độ UITextViewthay đổi kích thước UITableViewCelltrong khi chỉnh sửa

Nếu bạn đang làm việc với chế độ xem bảng tĩnh hoặc bạn chỉ có một số UITextViews đã biết, bạn có thể thực hiện bước 2 đơn giản hơn nhiều.

1. Đầu tiên, hãy ghi đè 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. Xác định hàm tính toán chiều cao cần thiết:

Thêm một NSMutableDictionary(trong ví dụ này được gọi là textViews) dưới dạng một biến thể hiện vào UITableViewControllerlớp con của bạn .

Sử dụng từ điển này để lưu trữ các tham chiếu đến cá nhân UITextViewsnhư vậy:

(và vâng, indexPath là khóa hợp lệ cho từ điển )

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

Hàm này bây giờ sẽ tính toán chiều cao thực tế:

- (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. Bật Thay đổi kích thước trong khi Chỉnh sửa

Đối với hai hàm tiếp theo, điều quan trọng là đại biểu của hàm UITextViewsđược đặt thành của bạn UITableViewController. Nếu bạn cần một cái gì đó khác với tư cách là người được ủy quyền, bạn có thể giải quyết vấn đề đó bằng cách thực hiện các lệnh gọi liên quan từ đó hoặc sử dụng các móc NSNotificationCenter thích hợp.

- (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. Theo dõi con trỏ trong khi Chỉnh sửa

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

Điều này sẽ làm cho UITableViewcuộn đến vị trí của con trỏ, nếu nó không nằm trong Rect có thể nhìn thấy của 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. Điều chỉnh trực tràng có thể nhìn thấy, bằng cách cài đặt các bộ phận

Trong khi chỉnh sửa, các phần của bạn UITableViewcó thể bị Bàn phím che mất. Nếu nội dung của chế độ xem bảng không được điều chỉnh, scrollToCursorForTextView:sẽ không thể cuộn đến con trỏ của bạn, nếu con trỏ ở cuối chế độ xem bảng.

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

Và phần cuối cùng:

Bên trong chế độ xem của bạn đã tải, hãy đăng ký Thông báo cho các thay đổi trên Bàn phím thông qua 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];
}

Xin đừng giận tôi, vì đã làm câu trả lời này quá lâu. Mặc dù không phải tất cả đều cần thiết để trả lời câu hỏi, nhưng tôi tin rằng có những người khác, những người mà những vấn đề liên quan trực tiếp này sẽ hữu ích.


CẬP NHẬT:

Như Dave Haupert đã chỉ ra, tôi đã quên bao gồm rectVisiblehàm:

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

Ngoài ra, tôi nhận thấy, điều đó scrollToCursorForTextView:vẫn bao gồm một tham chiếu trực tiếp đến một trong các Trường Văn bản trong dự án của tôi. Nếu bạn gặp sự cố bodyTextViewkhông tìm thấy, hãy kiểm tra phiên bản cập nhật của chức năng.


1
Mã đó đang hoạt động tốt ở tất cả! Nó thay đổi kích thước mọi thứ! Tuy nhiên, TextView của tôi luôn có chiều cao là 30px! Có cài đặt nào tôi không được phép đặt hoặc có cài đặt nào tôi không được phép trong UITextView không?
MyJBMe 19/09/13

1
Giải pháp này dường như không hoạt động trên bản sao và dán nếu văn bản lớn, bất kỳ ý tưởng nào?
Vikings

2
@Tim Bodeit, giải pháp của bạn hiệu quả, cảm ơn bạn! Nhưng tôi nghĩ bạn nên lưu ý trong nhận xét, rằng việc gán attributedTextmà không chỉ định phông chữ, màu sắc và căn chỉnh văn bản dẫn đến việc thiết lập các giá trị mặc định của thuộc tính NSAttributedString cho textView. Trong trường hợp của tôi, nó gây ra các chiều cao khác nhau của chế độ xem văn bản cho cùng một văn bản.
Alexander

4
Đây là một trong những câu trả lời Stack Overflow yêu thích nhất mọi thời đại của tôi - cảm ơn!
Richard Venable

3
@TimBodeit: Tôi không thể làm cho điều này hoạt động trên iOS8. Xin vui lòng cho tôi biết làm thế nào điều này có thể được sửa chữa.
Arun Gupta

37

Có một chức năng mới thay thế sizeWithFont, đó là bindingRectWithSize.

Tôi đã thêm hàm sau vào dự án của mình, hàm này sử dụng hàm mới trên iOS7 và hàm cũ trên iOS thấp hơn 7. Về cơ bản, nó có cùng cú pháp với 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
        }
    }

Bạn có thể thêm IOS_NEWER_OR_EQUAL_TO_7 vào tệp prefix.pch trong dự án của mình dưới dạng:

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

UITextViews của tôi vẫn không mở rộng quy mô khá tốt và không thể cuộn được khi văn bản kéo dài 3 dòng; pastebin.com/Wh6vmBqh
Martin de Keijzer

Câu lệnh trả về thứ hai cũng đưa ra cảnh báo không dùng nữa trong XCode.
Martin de Keijzer

Bạn cũng đang đặt kích thước của UItextView thành kích thước được tính toán của văn bản, trong cellForRowAtIndexPath? Ngoài ra, bạn không nên lo lắng về cảnh báo trong lần trả lại thứ hai vì nó chỉ được sử dụng khi ứng dụng được chạy trên thiết bị iOS6 mà chức năng này không bị phản đối.
mancosta

Bạn có thể cung cấp một ví dụ đơn giản về cách sử dụng chức năng này không?
Zorayr

@manecosta Tài liệu của Apple nói rằng bạn phải 'kết thúc' kết quả: Trong iOS 7 trở lên, phương thức này trả về kích thước phân số (trong thành phần kích thước của CGRect được trả về); để sử dụng kích thước trả về kích thước cho các khung nhìn kích thước, bạn phải sử dụng nâng giá trị của nó lên số nguyên cao hơn gần nhất bằng cách sử dụng hàm ceil.
HpTerm

9

Nếu bạn đang sử dụng UITableViewAutomaticDimension, tôi có một giải pháp thực sự đơn giản (chỉ dành cho iOS 8). Trong trường hợp của tôi, đó là chế độ xem bảng tĩnh, nhưng tôi đoán bạn có thể điều chỉnh điều này cho các nguyên mẫu động ...

Tôi có một lối ra hạn chế cho chiều cao của chế độ xem văn bản và tôi đã triển khai các phương pháp sau như thế này:

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

Nhưng hãy nhớ : chế độ xem văn bản phải có thể cuộn được và bạn phải thiết lập các ràng buộc của mình để chúng hoạt động cho thứ nguyên tự động:

  • thiết lập tất cả chế độ xem trong ô tương quan với nhau, với độ cao cố định (bao gồm cả độ cao của chế độ xem văn bản, mà bạn sẽ thay đổi theo chương trình)
  • chế độ xem trên cùng có khoảng cách trên cùng và chế độ xem dưới cùng có khoảng cách dưới cùng đến chế độ xem siêu;

Ví dụ ô cơ bản nhất là:

  • không có chế độ xem nào khác trong ô ngoại trừ chế độ xem văn bản
  • 0 lề xung quanh tất cả các mặt của chế độ xem văn bản và giới hạn chiều cao được xác định trước cho chế độ xem văn bản.

1
chế độ xem văn bản KHÔNG được cuộn
Akshit Zaveri

Tôi luôn nhận được cùng một kích thước dưới updateTextviewHeight. Có vẻ như kích thước nội dung bị sai. Tính năng cuộn bị tắt.
Dvole

5

Câu trả lời của Tim Bodeit là tuyệt vời. Tôi đã sử dụng mã của Giải pháp Đơn giản để lấy chính xác chiều cao của chế độ xem văn bản và sử dụng chiều cao đó trong heightForRowAtIndexPath. Nhưng tôi không sử dụng phần còn lại của câu trả lời để thay đổi kích thước chế độ xem văn bản. Thay vào đó, tôi viết mã để thay đổi framechế độ xem văn bản trong cellForRowAtIndexPath.

Mọi thứ đang hoạt động trong iOS 6 trở xuống, nhưng trong iOS 7, văn bản trong chế độ xem văn bản không thể được hiển thị đầy đủ mặc dù framechế độ xem văn bản thực sự đã được thay đổi kích thước. (Tôi không sử dụng Auto Layout). Đó phải là lý do mà trong iOS 7 có TextKitvà vị trí của văn bản được kiểm soát bởi NSTextContainertrong UITextView. Vì vậy, trong trường hợp của tôi, tôi cần thêm một dòng để thiết lập someTextViewnhằm làm cho nó hoạt động chính xác trong iOS 7.

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

Như tài liệu đã nói, những gì thuộc tính đó làm là:

Kiểm soát xem người nhận có điều chỉnh chiều cao của hình chữ nhật giới hạn khi chế độ xem văn bản của nó được thay đổi kích thước hay không. Giá trị mặc định: KHÔNG.

Nếu để nó với giá trị mặc định, sau khi thay đổi kích thước framecủa someTextView, kích thước của textContainerkhông bị thay đổi, dẫn đến kết quả là văn bản chỉ có thể được hiển thị trong vùng trước khi thay đổi kích thước.

Và có thể cần phải đặt scrollEnabled = NOtrường hợp có nhiều hơn một textContainer, để văn bản sẽ chỉnh sửa từ cái này textContainersang cái khác.


4

Dưới đây là một giải pháp khác nhằm mục đích đơn giản và tạo mẫu nhanh :

Thiết lập:

  1. Bảng với các ô nguyên mẫu.
  2. Mỗi ô chứa UITextViewcác nội dung khác có kích thước động .
  3. Các tế bào nguyên mẫu được liên kết với TableCell.h.
  4. UITableViewđược liên kết với TableViewController.h.

Giải pháp:

(1) Thêm vào 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) Thêm vào 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;
}

Giải trình:

Vì vậy, những gì đang xảy ra ở đây là: mỗi chế độ xem văn bản được ràng buộc với chiều cao của các ô trong bảng bởi các ràng buộc dọc và ngang - điều đó có nghĩa là khi chiều cao của ô trong bảng tăng lên, chế độ xem văn bản cũng tăng kích thước của nó. Tôi đã sử dụng một phiên bản sửa đổi của mã @ Mahcosta để tính toán chiều cao cần thiết của chế độ xem văn bản để vừa với văn bản đã cho trong một ô. Vì vậy, điều đó có nghĩa là được cung cấp một văn bản có số ký tự X, frameForText:sẽ trả về kích thước sẽ có thuộc tính size.heightphù hợp với chiều cao cần thiết của chế độ xem văn bản.

Bây giờ, tất cả những gì còn lại là cập nhật chiều cao của ô để phù hợp với chiều cao của chế độ xem văn bản được yêu cầu. Và điều này đạt được tại heightForRowAtIndexPath:. Như đã lưu ý trong các nhận xét, vì size.heightchỉ là chiều cao cho chế độ xem văn bản chứ không phải toàn bộ ô, nên có một số bù đắp được thêm vào nó. Trong trường hợp của ví dụ, giá trị này là 80.


'Dream.dream' này là viết tắt của gì?
MyJBMe

@MyJBMe xin lỗi vì đó là một phần trong dự án của riêng tôi - tôi đã cập nhật mã cho phù hợp. dream.dreamlà văn bản mà tôi đang hiển thị trong chế độ xem văn bản.
Zorayr

3

Một cách tiếp cận nếu bạn đang sử dụng autolayout là để công cụ autolayout tính toán kích thước cho bạn. Đây không phải là cách tiếp cận hiệu quả nhất nhưng nó khá tiện lợi (và được cho là chính xác nhất). Nó trở nên thuận tiện hơn khi độ phức tạp của bố cục ô tăng lên - ví dụ: đột nhiên bạn có hai hoặc nhiều textview / trường trong ô.

Tôi đã trả lời một câu hỏi tương tự với một mẫu hoàn chỉnh để định cỡ ô trong chế độ xem bảng bằng cách sử dụng bố cục tự động, tại đây:

Làm cách nào để thay đổi kích thước superview để phù hợp với tất cả các subview với tính năng tự động thanh toán?


1

Giải pháp hoàn toàn trơn tru như sau.

Đầu tiên, chúng ta cần lớp ô có textView

@protocol TextInputTableViewCellDelegate <NSObject>
@optional
- (void)textInputTableViewCellTextWillChange:(TextInputTableViewCell *)cell;
- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell;
@end

@interface TextInputTableViewCell : UITableViewCell
@property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
@property (nonatomic, readonly) UITextView *textView;
@property (nonatomic) NSInteger minLines;
@property (nonatomic) CGFloat lastRelativeFrameOriginY;
@end


#import "TextInputTableViewCell.h"

@interface TextInputTableViewCell () <UITextViewDelegate> {
    NSLayoutConstraint *_heightConstraint;
}
@property (nonatomic) UITextView *textView;
@end

@implementation TextInputTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;

        _textView = [UITextView new];
        _textView.translatesAutoresizingMaskIntoConstraints = NO;
        _textView.delegate = self;
        _textView.scrollEnabled = NO;
        _textView.font = CELL_REG_FONT;
        _textView.textContainer.lineFragmentPadding = 0.0;
        _textView.textContainerInset = UIEdgeInsetsZero;
        [self.contentView addSubview:_textView];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];
        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];

        _heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
                         attribute: NSLayoutAttributeHeight
                         relatedBy: NSLayoutRelationGreaterThanOrEqual
                         toItem: nil
                         attribute: NSLayoutAttributeNotAnAttribute
                         multiplier: 0.0
                         constant: (_textView.font.lineHeight + 15)];
        _heightConstraint.priority = UILayoutPriorityRequired - 1;
        [_textView addConstraint:_heightConstraint];
    }
    return self;
}

- (void)prepareForReuse {
    [super prepareForReuse];    
    self.minLines = 1;
}

- (void)setMinLines:(NSInteger)minLines {
    _heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if ([self.delegate respondsToSelector:@selector(textInputTableViewCellTextWillChange:)]) {
        [self.delegate textInputTableViewCellTextWillChange:self];
    }
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {
    if ([self.delegate respondsToSelector:@selector(textInputTableViewCellTextDidChange:)]) {
        [self.delegate textInputTableViewCellTextDidChange:self];
    }
}

Tiếp theo, chúng tôi sử dụng nó trong 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
  • Ở đây minLinescho phép thiết lập chiều cao tối thiểu cho textView (để chống lại việc giảm thiểu chiều cao bằng AutoLayout với UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: với cùng một indexPath bắt đầu tính toán lại chiều cao tableViewCell và bố trí lại.

  • performWithoutAnimation: loại bỏ hiệu ứng phụ (nhảy bù nội dung tableView khi bắt đầu dòng mới trong khi nhập).

  • Điều quan trọng là phải bảo toàn relativeFrameOriginY(không contentOffsetY!) Trong quá trình cập nhật ô vì contentSizecác ô trước ô hiện tại có thể bị thay đổi bằng phép tính Tự động thanh toán theo cách không mong muốn. Nó loại bỏ các bước nhảy trực quan trên gạch nối hệ thống trong khi gõ các từ dài.

  • Lưu ý rằng bạn không nên đặt thuộc tính estimatedRowHeight ! Sau đây không hoạt động

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    Chỉ sử dụng phương thức tableViewDelegate.

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

Nếu ai đó không ngại liên kết yếu giữa tableViewtableViewCell và cập nhật hình học của tableView từ tableViewCell , thì có thể nâng cấp TextInputTableViewCelllớp ở trên:

@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. Đặt UILabel sau UITextView của bạn.
  2. Sử dụng câu trả lời sau: https://stackoverflow.com/a/36054679/6681462 để UILabel bạn đã tạo
  3. Cung cấp cho họ những ràng buộc và phông chữ giống nhau
  4. Đặt chúng cùng một văn bản;

Chiều cao ô của bạn sẽ tính theo nội dung của UILabel, nhưng tất cả văn bản sẽ được hiển thị bởi TextField.


0
UITextView *txtDescLandscape=[[UITextView alloc] initWithFrame:CGRectMake(2,20,310,2)];

    txtDescLandscape.editable =NO;
    txtDescLandscape.textAlignment =UITextAlignmentLeft;
    [txtDescLandscape setFont:[UIFont fontWithName:@"ArialMT" size:15]];
    txtDescLandscape.text =[objImage valueForKey:@"imgdescription"];
    txtDescLandscape.text =[txtDescLandscape.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    [txtDescLandscape sizeToFit];
    [headerView addSubview:txtDescLandscape];

    CGRect txtViewlandscpframe = txtDescLandscape.frame;
    txtViewlandscpframe.size.height = txtDescLandscape.contentSize.height;
    txtDescLandscape.frame = txtViewlandscpframe;

Tôi nghĩ theo cách này, bạn có thể đếm chiều cao của chế độ xem văn bản và sau đó thay đổi kích thước ô trong chế độ xem bảng của bạn theo chiều cao đó để bạn có thể hiển thị toàn văn trên ô


0

Phiên bản 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
}

0

Nếu bạn muốn tự động điều chỉnh UITableViewCellchiều cao của dựa trên chiều cao của chiều cao bên trong UITextView. Xem câu trả lời của tôi tại đây: https://stackoverflow.com/a/45890087/1245231

Giải pháp khá đơn giản và sẽ hoạt động kể từ iOS 7. Đảm bảo rằng Scrolling Enabledtùy chọn đã được tắt cho UITextViewbên UITableViewCelltrong StoryBoard.

Sau đó, trong viewDidLoad () của UITableViewController, hãy đặt tableView.rowHeight = UITableViewAutomaticDimensiontableView.estimatedRowHeight > 0chẳng hạn như:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

Đó là nó. UITableViewCellChiều cao của sẽ được tự động điều chỉnh dựa trên UITextViewchiều cao của bên trong .


-2

Đối với iOS 8 trở lên, bạn chỉ có thể sử dụng

your_tablview.estimatedrowheight= minheight bạn muốn

your_tableview.rowheight=UItableviewautomaticDimension
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.