UITableViewCell ที่มีความสูง UITextView ใน iOS 7?


121

ฉันจะคำนวณความสูงของ UITableViewCell ด้วย UITextView ใน iOS 7 ได้อย่างไร

ฉันพบคำตอบมากมายสำหรับคำถามที่คล้ายกัน แต่sizeWithFont:มีส่วนร่วมในทุกวิธีแก้ปัญหาและวิธีนี้เลิกใช้แล้ว!

ฉันรู้ว่าฉันต้องใช้- (CGFloat)tableView:heightForRowAtIndexPath:แต่ฉันจะคำนวณความสูงที่ TextView ของฉันต้องใช้เพื่อแสดงข้อความทั้งหมดได้อย่างไร

คำตอบ:


428

ก่อนอื่นสิ่งสำคัญคือต้องทราบว่ามีความแตกต่างอย่างมากระหว่าง UITextView และ UILabel เมื่อพูดถึงวิธีการแสดงผลข้อความ UITextView ไม่เพียง แต่มีการแทรกในเส้นขอบทั้งหมด แต่ยังรวมถึงเค้าโครงข้อความที่อยู่ภายในนั้นแตกต่างกันเล็กน้อย

ดังนั้นจึงsizeWithFont:เป็นวิธีที่ไม่ดีสำหรับ UITextViews

แทนUITextViewตัวเองมีฟังก์ชั่นที่เรียกว่าsizeThatFits:ซึ่งจะกลับมาขนาดที่เล็กที่สุดที่จำเป็นในการแสดงเนื้อหาทั้งหมดของUITextViewภายในกล่องขอบเขตที่คุณสามารถระบุ

สิ่งต่อไปนี้จะใช้ได้กับทั้ง iOS 7 และเวอร์ชันเก่ากว่าและ ณ ตอนนี้ยังไม่มีวิธีการใด ๆ ที่เลิกใช้งานแล้ว


วิธีแก้ปัญหาง่ายๆ

- (CGFloat)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width {
    UITextView *calculationView = [[UITextView alloc] init];
    [calculationView setAttributedText:text];
    CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
    return size.height;
}

ฟังก์ชันนี้จะใช้ a NSAttributedStringและความกว้างที่ต้องการเป็น a CGFloatและส่งกลับความสูงที่ต้องการ


โซลูชันโดยละเอียด

เนื่องจากฉันเพิ่งทำสิ่งที่คล้ายกันเมื่อไม่นานมานี้ฉันคิดว่าฉันจะแบ่งปันวิธีแก้ปัญหาบางอย่างเกี่ยวกับปัญหาที่เชื่อมต่อที่ฉันพบ ฉันหวังว่ามันจะช่วยใครสักคน

นี่เป็นข้อมูลเชิงลึกมากขึ้นและจะครอบคลุมสิ่งต่อไปนี้:

  • แน่นอน: การตั้งค่าความสูงUITableViewCellตามขนาดที่จำเป็นในการแสดงเนื้อหาทั้งหมดของบรรจุภัณฑ์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;           
}

2. กำหนดฟังก์ชันที่คำนวณความสูงที่ต้องการ:

เพิ่มNSMutableDictionary(ในตัวอย่างนี้เรียกว่าtextViews) เป็นตัวแปรอินสแตนซ์ให้กับUITableViewControllerคลาสย่อยของคุณ

ใช้พจนานุกรมนี้เพื่อจัดเก็บการอ้างอิงถึงบุคคลUITextViewsเช่นนี้:

(และใช่indexPaths เป็นคีย์ที่ถูกต้องสำหรับพจนานุกรม )

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Do you cell configuring ...

    [textViews setObject:cell.textView forKey:indexPath];
    [cell.textView setDelegate: self]; // Needed for step 3

    return cell;
}

ฟังก์ชั่นนี้จะคำนวณความสูงจริง:

- (CGFloat)textViewHeightForRowAtIndexPath: (NSIndexPath*)indexPath {
    UITextView *calculationView = [textViews objectForKey: indexPath];
    CGFloat textViewWidth = calculationView.frame.size.width;
    if (!calculationView.attributedText) {
        // This will be needed on load, when the text view is not inited yet
        
        calculationView = [[UITextView alloc] init];
        calculationView.attributedText = // get the text from your datasource add attributes and insert here
        textViewWidth = 290.0; // Insert the width of your UITextViews or include calculations to set it accordingly
    }
    CGSize size = [calculationView sizeThatFits:CGSizeMake(textViewWidth, FLT_MAX)];
    return size.height;
}

3. เปิดใช้งานการปรับขนาดขณะแก้ไข

สำหรับสองฟังก์ชั่นถัดไปสิ่งสำคัญคือผู้รับมอบสิทธิ์UITextViewsถูกตั้งค่าเป็นUITableViewControllerไฟล์. หากคุณต้องการสิ่งอื่นในฐานะผู้รับมอบสิทธิ์คุณสามารถแก้ไขได้โดยการโทรที่เกี่ยวข้องจากที่นั่นหรือใช้ตะขอ NSNotificationCenter ที่เหมาะสม

- (void)textViewDidChange:(UITextView *)textView {

    [self.tableView beginUpdates]; // This will cause an animated update of
    [self.tableView endUpdates];   // the height of your UITableViewCell

    // If the UITextView is not automatically resized (e.g. through autolayout 
    // constraints), resize it here

    [self scrollToCursorForTextView:textView]; // OPTIONAL: Follow cursor
}

4. ทำตามเคอร์เซอร์ขณะแก้ไข

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

สิ่งนี้จะทำให้การUITableViewเลื่อนไปยังตำแหน่งของเคอร์เซอร์หากไม่ได้อยู่ใน Rect ที่มองเห็นได้ของ 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. ปรับ rect ที่มองเห็นได้โดยการตั้งค่าสิ่งที่ใส่เข้าไป

ในขณะที่แก้ไขบางส่วนของคุณUITableViewอาจถูกปิดทับโดยแป้นพิมพ์ หากไม่ได้ปรับค่าที่แทรกของ tableviews 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];
}

และส่วนสุดท้าย:

ภายในมุมมองของคุณโหลดลงทะเบียนการแจ้งเตือนสำหรับแป้นพิมพ์ผ่าน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];
}

โปรดอย่าโกรธฉันที่ตอบคำถามนี้มานาน แม้ว่าจะไม่จำเป็นต้องตอบคำถามทั้งหมด แต่ฉันเชื่อว่ามีคนอื่น ๆ ที่เกี่ยวข้องโดยตรงกับปัญหาเหล่านี้จะเป็นประโยชน์


UPDATE:

ดังที่ Dave Haupert ชี้ให้เห็นฉันลืมใส่rectVisibleฟังก์ชัน:

- (BOOL)rectVisible: (CGRect)rect {
    CGRect visibleRect;
    visibleRect.origin = self.tableView.contentOffset;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size = self.tableView.bounds.size;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
    
    return CGRectContainsRect(visibleRect, rect);
}

นอกจากนี้ฉันสังเกตเห็นว่าscrollToCursorForTextView:ยังมีการอ้างอิงโดยตรงไปยัง TextFields หนึ่งในโครงการของฉัน หากคุณมีปัญหากับการbodyTextViewไม่พบให้ตรวจสอบเวอร์ชันอัปเดตของฟังก์ชัน


1
รหัสนั้นใช้งานได้ดีเลย! มันปรับขนาดทุกอย่าง! แต่ TextView ของฉันมีความสูง 30px เสมอ! มีการตั้งค่าที่ฉันไม่ได้รับอนุญาตให้ตั้งค่าหรือมีบางอย่างที่ฉันไม่ได้รับอนุญาตใน UITextView?
MyJBMe

1
โซลูชันนี้ดูเหมือนจะใช้ไม่ได้กับการคัดลอกและวางหากข้อความมีขนาดใหญ่ความคิดใด ๆ
ไวกิ้ง

2
@ Tim Bodeit วิธีแก้ปัญหาของคุณได้ผลขอบคุณ! แต่ฉันคิดว่าคุณควรสังเกตในความคิดเห็นว่าการกำหนดattributedTextโดยไม่ระบุแบบอักษรสีและการจัดแนวข้อความนำไปสู่การตั้งค่าเริ่มต้นของแอตทริบิวต์ NSAttributedString ให้กับ textView ในกรณีของฉันมันทำให้เกิดความสูงที่แตกต่างกันของ textview สำหรับข้อความเดียวกัน
Alexander

4
นี่เป็นหนึ่งในคำตอบ Stack Overflow ที่ฉันชอบตลอดกาล - ขอบคุณ!
Richard Venable

3
@TimBodeit: ฉันไม่สามารถทำงานนี้บน iOS8 ได้ โปรดแจ้งให้เราทราบว่าจะแก้ไขได้อย่างไร
อรุณคุปตะ

37

มีฟังก์ชันใหม่เพื่อแทนที่ sizeWithFont ซึ่งเป็น boundingRectWithSize

ฉันเพิ่มฟังก์ชั่นต่อไปนี้ในโปรเจ็กต์ของฉันซึ่งใช้ประโยชน์จากฟังก์ชันใหม่บน iOS7 และฟังก์ชันเก่าบน iOS ที่ต่ำกว่า 7 โดยทั่วไปมีไวยากรณ์เดียวกับ sizeWithFont:

    -(CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{
        if(IOS_NEWER_OR_EQUAL_TO_7){
            NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                              font, NSFontAttributeName,
                                              nil];

            CGRect frame = [text boundingRectWithSize:size
                                              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                           attributes:attributesDictionary
                                              context:nil];

            return frame.size;
        }else{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
            return [text sizeWithFont:font constrainedToSize:size];
#pragma clang diagnostic pop
        }
    }

คุณสามารถเพิ่ม IOS_NEWER_OR_EQUAL_TO_7 ในไฟล์ prefix.pch ในโปรเจ็กต์ของคุณเป็น:

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

UITextViews ของฉันยังปรับขนาดได้ไม่ดีนักและเลื่อนได้เมื่อข้อความมีความยาว 3 บรรทัด pastebin.com/Wh6vmBqh
Martin de Keijzer

คำสั่ง return ที่สองยังแสดงคำเตือนการเลิกใช้งานใน XCode
Martin de Keijzer

คุณกำลังตั้งค่าขนาดของ UItextView เป็นขนาดที่คำนวณได้ของข้อความใน cellForRowAtIndexPath หรือไม่ นอกจากนี้คุณไม่ควรกังวลเกี่ยวกับคำเตือนในการส่งคืนครั้งที่สองเนื่องจากจะใช้เฉพาะเมื่อแอปทำงานบนอุปกรณ์ iOS6 ซึ่งไม่มีการเลิกใช้งานฟังก์ชันนี้
manecosta

คุณช่วยยกตัวอย่างง่ายๆในการใช้ฟังก์ชันนี้ได้หรือไม่?
Zorayr

เอกสารของ @manecosta Apple ระบุว่าคุณต้อง 'ceil' ผลลัพธ์: ใน iOS 7 ขึ้นไปวิธีนี้จะส่งคืนขนาดเศษส่วน (ในส่วนประกอบขนาดของ CGRect ที่ส่งคืน) ในการใช้มุมมองขนาดที่ส่งคืนเป็นขนาดคุณต้องใช้เพิ่มค่าเป็นจำนวนเต็มที่ใกล้เคียงที่สุดโดยใช้ฟังก์ชัน ceil
HpTerm

9

หากคุณใช้ UITableViewAutomaticDimension ฉันมีวิธีแก้ปัญหาที่ง่ายมาก (iOS 8 เท่านั้น) ในกรณีของฉันมันเป็นมุมมองตารางคงที่ แต่ฉันเดาว่าคุณสามารถปรับสิ่งนี้สำหรับต้นแบบไดนามิกได้ ...

ฉันมีข้อ จำกัด สำหรับความสูงของมุมมองข้อความและฉันได้ใช้วิธีการต่อไปนี้:

// Outlets

@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeight;


// Implementation

#pragma mark - Private Methods

- (void)updateTextViewHeight {
    self.textViewHeight.constant = self.textView.contentSize.height + self.textView.contentInset.top + self.textView.contentInset.bottom;
}

#pragma mark - View Controller Overrides

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateTextViewHeight];
}

#pragma mark - TableView Delegate & Datasource

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 80;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

#pragma mark - TextViewDelegate

- (void)textViewDidChange:(UITextView *)textView {
    [self.tableView beginUpdates];
    [self updateTextViewHeight];
    [self.tableView endUpdates];
}

แต่โปรดจำไว้ว่า : มุมมองข้อความต้องเลื่อนได้และคุณต้องตั้งค่าข้อ จำกัด ของคุณเพื่อให้สามารถใช้กับมิติข้อมูลอัตโนมัติได้:

  • ตั้งค่ามุมมองทั้งหมดในเซลล์ให้สัมพันธ์กันโดยมีความสูงคงที่ (รวมถึงความสูงของมุมมองข้อความซึ่งคุณจะเปลี่ยนตามโปรแกรม)
  • มุมมองส่วนใหญ่ด้านบนมีระยะห่างด้านบนและมุมมองส่วนใหญ่ด้านล่างมีระยะห่างด้านล่างถึงมุมมองขั้นสูง

ตัวอย่างเซลล์พื้นฐานที่สุดคือ:

  • ไม่มีมุมมองอื่น ๆ ในเซลล์ยกเว้นมุมมองข้อความ
  • 0 ระยะขอบรอบทุกด้านของมุมมองข้อความและข้อ จำกัด ด้านความสูงที่กำหนดไว้ล่วงหน้าสำหรับมุมมองข้อความ

1
มุมมองข้อความจะต้องไม่สามารถเลื่อนได้
Akshit Zaveri

ฉันได้รับขนาดเดียวกันภายใต้ updateTextviewHeight ตลอดเวลา ดูเหมือนขนาดเนื้อหาจะไม่ถูกต้อง การเลื่อนถูกปิดใช้งาน
Dvole

5

คำตอบของ Tim Bodeit นั้นยอดเยี่ยมมาก ฉันใช้รหัสของ Simple Solution เพื่อรับความสูงของมุมมองข้อความอย่างถูกต้องและใช้ความสูงนั้นในheightForRowAtIndexPath. แต่ฉันไม่ใช้คำตอบที่เหลือเพื่อปรับขนาดมุมมองข้อความ แต่ฉันเขียนโค้ดเพื่อเปลี่ยนframeมุมมองข้อความในcellForRowAtIndexPath.

ทุกอย่างใช้งานได้ใน iOS 6 และต่ำกว่า แต่ใน iOS 7 ข้อความในมุมมองข้อความไม่สามารถแสดงได้อย่างสมบูรณ์แม้ว่าframeมุมมองข้อความจะถูกปรับขนาดแล้วก็ตาม (ฉันไม่ได้ใช้Auto Layout) มันควรจะเป็นเหตุผลที่ว่าใน iOS 7 มีTextKitและตำแหน่งของข้อความจะถูกควบคุมโดยในNSTextContainer UITextViewดังนั้นในกรณีของฉันฉันต้องเพิ่มบรรทัดเพื่อตั้งค่าsomeTextViewเพื่อให้มันทำงานได้อย่างถูกต้องใน iOS 7

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

ตามที่เอกสารกล่าวไว้คุณสมบัตินั้นคืออะไร:

ควบคุมว่าเครื่องรับจะปรับความสูงของรูปสี่เหลี่ยมล้อมรอบหรือไม่เมื่อปรับขนาดมุมมองข้อความ ค่าดีฟอลต์: NO.

หากปล่อยทิ้งไว้ไม่ค่าเริ่มต้นหลังจากที่ปรับขนาดframeของsomeTextViewขนาดของที่textContainerจะไม่มีการเปลี่ยนแปลงที่นำไปสู่ผลที่ได้ว่าข้อความที่สามารถแสดงผลเฉพาะในพื้นที่ก่อนการปรับขนาด

และอาจจำเป็นต้องตั้งค่าscrollEnabled = NOในกรณีที่มีมากกว่าหนึ่งtextContainerข้อความเพื่อให้ข้อความtextContainerแสดงซ้ำจากที่หนึ่งไปอีกข้อความหนึ่ง


4

นี่คืออีกหนึ่งวิธีแก้ปัญหาที่มุ่งเน้นไปที่ความเรียบง่ายและการสร้างต้นแบบอย่างรวดเร็ว :

ติดตั้ง:

  1. ตารางที่มีเซลล์ต้นแบบ
  2. แต่ละเซลล์มีขนาดไดนามิกพร้อมUITextViewเนื้อหาอื่น ๆ
  3. TableCell.hเซลล์ต้นแบบที่เกี่ยวข้องกับ
  4. UITableViewTableViewController.hมีความเกี่ยวข้องกับ

สารละลาย:

(1) เพิ่มในTableViewController.m:

 // This is the method that determines the height of each cell.  
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    // I am using a helper method here to get the text at a given cell.
    NSString *text = [self getTextAtIndex:indexPath];

    // Getting the height needed by the dynamic text view.
    CGSize size = [self frameForText:text sizeWithFont:nil constrainedToSize:CGSizeMake(300.f, CGFLOAT_MAX)];

    // Return the size of the current row.
    // 80 is the minimum height! Update accordingly - or else, cells are going to be too thin.
    return size.height + 80; 
}

// Think of this as some utility function that given text, calculates how much 
// space would be needed to fit that text.
- (CGSize)frameForText:(NSString *)text sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
{
    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          font, NSFontAttributeName,
                                          nil];
    CGRect frame = [text boundingRectWithSize:size
                                      options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                   attributes:attributesDictionary
                                      context:nil];

    // This contains both height and width, but we really care about height.
    return frame.size;
}

// Think of this as a source for the text to be rendered in the text view. 
// I used a dictionary to map indexPath to some dynamically fetched text.
- (NSString *) getTextAtIndex: (NSIndexPath *) indexPath
{
    return @"This is stubbed text - update it to return the text of the text view.";
}

(2) เพิ่มในTableCell.m:

// This method will be called when the cell is initialized from the storyboard
// prototype. 
- (void)awakeFromNib
{
    // Assuming TextView here is the text view in the cell. 
    TextView.scrollEnabled = YES;
}

คำอธิบาย:

สิ่งที่เกิดขึ้นที่นี่คือมุมมองข้อความแต่ละมุมมองจะถูกผูกไว้กับความสูงของเซลล์ตารางตามข้อ จำกัด แนวตั้งและแนวนอนนั่นหมายความว่าเมื่อความสูงของเซลล์ตารางเพิ่มขึ้นมุมมองข้อความจะเพิ่มขนาดตามไปด้วย ฉันใช้โค้ดของ @ manecosta เวอร์ชันแก้ไขเพื่อคำนวณความสูงที่ต้องการของมุมมองข้อความเพื่อให้พอดีกับข้อความที่กำหนดในเซลล์ นั่นหมายความว่าให้ข้อความที่มีจำนวนอักขระ X frameForText:จะส่งกลับขนาดซึ่งจะมีคุณสมบัติsize.heightที่ตรงกับความสูงที่ต้องการของมุมมองข้อความ

ตอนนี้สิ่งที่เหลืออยู่คือการอัปเดตความสูงของเซลล์ให้ตรงกับความสูงของมุมมองข้อความที่ต้องการ heightForRowAtIndexPath:และนี่คือความสำเร็จที่ ตามที่ระบุไว้ในความคิดเห็นเนื่องจากsize.heightเป็นเพียงความสูงของมุมมองข้อความไม่ใช่ทั้งเซลล์จึงควรมีการเพิ่มออฟเซ็ตลงไป ในกรณีของตัวอย่างค่านี้คือ 80


คำว่า 'dream.dream' นี้ย่อมาจากอะไร?
MyJBMe

@MyJBMe ขอโทษที่เป็นส่วนหนึ่งของโครงการของฉันเอง - ฉันได้อัปเดตรหัสตามนั้น dream.dreamเป็นข้อความที่ฉันแสดงในมุมมองข้อความ
Zorayr

3

แนวทางหนึ่งหากคุณใช้การจัดวางอัตโนมัติคือให้กลไกการจัดวางอัตโนมัติคำนวณขนาดให้คุณ นี่ไม่ใช่วิธีที่มีประสิทธิภาพที่สุด แต่ค่อนข้างสะดวก (และเนื้อหาถูกต้องที่สุด) จะสะดวกมากขึ้นเมื่อความซับซ้อนของโครงร่างเซลล์เพิ่มขึ้นเช่นจู่ๆคุณมี textviews / เขตข้อมูลสองรายการขึ้นไปในเซลล์

ฉันตอบคำถามที่คล้ายกันพร้อมกับตัวอย่างที่สมบูรณ์สำหรับการปรับขนาดเซลล์มุมมองตารางโดยใช้รูปแบบอัตโนมัติที่นี่:

วิธีปรับขนาดซูเปอร์วิวให้พอดีกับมุมมองย่อยทั้งหมดที่มีการจัดวางอัตโนมัติ


1

วิธีการแก้ปัญหาที่ราบรื่นสมบูรณ์มีดังนี้

ขั้นแรกเราต้องการคลาสเซลล์ที่มี textView

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

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


#import "TextInputTableViewCell.h"

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

@implementation TextInputTableViewCell

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

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

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

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

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

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

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

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

ต่อไปเราใช้ใน TableViewController

@interface SomeTableViewController () <TextInputTableViewCellDelegate>
@end

@implementation SomeTableViewController

. . . . . . . . . . . . . . . . . . . .

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    TextInputTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: TextInputTableViewCellIdentifier forIndexPath:indexPath];
    cell.delegate = self;
    cell.minLines = 3;
    . . . . . . . . . .  
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

- (void)textInputTableViewCellWillChange:(TextInputTableViewCell *)cell {
    cell.lastRelativeFrameOriginY = cell.frame.origin.y - self.tableView.contentOffset.y;
}

- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell {
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    [UIView performWithoutAnimation:^{
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
    }];

    CGFloat contentOffsetY = cell.frame.origin.y - cell.lastRelativeFrameOriginY;
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);

    CGRect caretRect = [cell.textView caretRectForPosition:cell.textView.selectedTextRange.start];
    caretRect = [self.tableView convertRect:caretRect fromView:cell.textView];

    CGRect visibleRect = self.tableView.bounds;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
    BOOL res = CGRectContainsRect(visibleRect, caretRect);
    if (!res) {
        caretRect.size.height += 5;
        [self.tableView scrollRectToVisible:caretRect animated:NO];
    }
}
@end
  • ที่นี่minLinesอนุญาตให้ตั้งค่าความสูงขั้นต่ำสำหรับ textView (เพื่อต้านทานการลดความสูงโดย AutoLayout ด้วย UITableViewAutomaticDimension)

  • moveRowAtIndexPath:indexPath: ด้วย indexPath เดียวกันเริ่มต้นการคำนวณความสูงของ tableViewCell และการจัดวางใหม่

  • performWithoutAnimation: ลบเอฟเฟกต์ด้านข้าง (การข้ามเนื้อหา tableView เมื่อเริ่มบรรทัดใหม่ขณะพิมพ์)

  • สิ่งสำคัญคือต้องสงวนไว้relativeFrameOriginY(ไม่ใช่ contentOffsetY!) ในระหว่างการอัปเดตเซลล์เนื่องจากcontentSizeเซลล์ก่อนที่เซลล์ปัจจุบันจะเปลี่ยนแปลงได้โดยแคลคูลัส autoLayout ด้วยวิธีที่ไม่คาดคิด จะลบภาพกระโดดในการยัติภังค์ของระบบในขณะที่พิมพ์คำยาว ๆ

  • โปรดทราบว่าคุณไม่ควรตั้งค่าคุณสมบัติ estimatedRowHeight ! สิ่งต่อไปนี้ใช้ไม่ได้

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    ใช้เมธอด tableViewDelegate เท่านั้น

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

หากไม่มีใครรังเกียจต่อการผูกที่อ่อนแอระหว่างtableViewและtableViewCellและการอัปเดตรูปทรงเรขาคณิตของ tableView จากtableViewCellคุณสามารถอัปเกรดTextInputTableViewCellคลาสด้านบนได้:

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


#import "TextInputTableViewCell.h"

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

@implementation TextInputTableViewCell

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

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

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

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

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

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

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

    _lastRelativeFrameOriginY = self.frame.origin.y - self.tableView.contentOffset.y;
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {

    NSIndexPath *indexPath = [self.tableView indexPathForCell:self];
    if (indexPath == nil) return;

    [UIView performWithoutAnimation:^{
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
    }];

    CGFloat contentOffsetY = self.frame.origin.y - _lastRelativeFrameOriginY;
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);

    CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.start];
    caretRect = [self.tableView convertRect:caretRect fromView:self.textView];

    CGRect visibleRect = self.tableView.bounds;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;

    BOOL res = CGRectContainsRect(visibleRect, caretRect);
    if (!res) {
        caretRect.size.height += 5;
        [self.tableView scrollRectToVisible:caretRect animated:NO];
    }
}
@end

1
  1. วาง UILabel ไว้ด้านหลัง UITextView ของคุณ
  2. ใช้คำตอบนี้: https://stackoverflow.com/a/36054679/6681462ถึง UILabel ที่คุณสร้างขึ้น
  3. กำหนดข้อ จำกัด และแบบอักษรให้เหมือนกัน
  4. ตั้งค่าข้อความเดียวกัน

ความสูงของเซลล์ของคุณจะคำนวณตามเนื้อหาของ UILabel แต่ TextField จะแสดงข้อความทั้งหมด


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

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

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

ฉันคิดว่าวิธีนี้คุณสามารถนับความสูงของมุมมองข้อความของคุณแล้วปรับขนาดเซลล์มุมมองตารางของคุณตามความสูงนั้นเพื่อให้คุณสามารถแสดงข้อความแบบเต็มบนเซลล์ได้


0

เวอร์ชัน 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

หากคุณต้องการที่จะปรับเปลี่ยนโดยอัตโนมัติUITableViewCell's ความสูงขึ้นอยู่กับความสูงของชั้นในUITextView' ความสูง ดูคำตอบของฉันที่นี่: https://stackoverflow.com/a/45890087/1245231

การแก้ปัญหาคือค่อนข้างง่ายและควรจะทำงานตั้งแต่ iOS ของคุณ 7. ให้แน่ใจว่าScrolling Enabledตัวเลือกที่มีการเปิดปิดสำหรับUITextViewภายในUITableViewCellในสตอรี่บอร์ด

จากนั้นใน viewDidLoad () ของ UITableViewController ของคุณตั้งค่าtableView.rowHeight = UITableViewAutomaticDimensionและtableView.estimatedRowHeight > 0เช่น:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

แค่นั้นแหละ. UITableViewCellความสูงของจะถูกปรับโดยอัตโนมัติตามUITextViewความสูงภายใน


-2

สำหรับ iOS 8 ขึ้นไปคุณสามารถใช้ได้

your_tablview.estimatedrowheight= minheight คุณต้องการ

your_tableview.rowheight=UItableviewautomaticDimension
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.