NSTableView ตามการดูที่มีแถวที่มีความสูงแบบไดนามิก


84

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

ฉันรู้ว่าฉันสามารถใช้NSTableViewDelegateวิธีนี้ได้ - tableView:heightOfRow:เพื่อคืนความสูง แต่ความสูงจะถูกกำหนดตามการตัดคำที่ใช้ในไฟล์NSTextField. การตัดคำในNSTextFieldทำนองเดียวกันขึ้นอยู่กับความกว้างของNSTextField... ซึ่งกำหนดโดยความกว้างของNSTableView.

เอาล่ะ…ฉันเดาว่าคำถามของฉันคือ…รูปแบบการออกแบบที่ดีสำหรับสิ่งนี้คืออะไร? ดูเหมือนว่าทุกสิ่งที่ฉันพยายามจะกลายเป็นเรื่องยุ่งเหยิงที่ซับซ้อน เนื่องจาก TableView ต้องการความรู้เกี่ยวกับความสูงของเซลล์ในการจัดวาง ... และNSTextFieldความต้องการความรู้เกี่ยวกับเลย์เอาต์ของมันเพื่อกำหนดการตัดคำ ... และเซลล์ต้องการความรู้เกี่ยวกับการตัดคำเพื่อกำหนดความสูง ... มันเป็นระเบียบแบบวงกลม ... และก็ขับรถฉันบ้า

ข้อเสนอแนะ?

หากเป็นเรื่องสำคัญผลลัพธ์สุดท้ายจะมีการแก้ไขNSTextFieldsซึ่งจะปรับขนาดเพื่อปรับให้เข้ากับข้อความภายใน ฉันมีสิ่งนี้ที่ใช้งานได้ในระดับมุมมองแล้ว แต่มุมมองตารางยังไม่ได้ปรับความสูงของเซลล์ ฉันคิดว่าเมื่อได้ปัญหาความสูงแล้วฉันจะใช้ - noteHeightOfRowsWithIndexesChangedวิธีการเพื่อแจ้งให้มุมมองของตารางทราบว่ามีการเปลี่ยนแปลงความสูง ... แต่ก็ยังคงถามผู้ร่วมประชุมเกี่ยวกับความสูง ... ด้วยเหตุนี้ความไม่แน่ใจของฉัน

ขอบคุณล่วงหน้า!


หากทั้งความกว้างและความสูงของมุมมองสามารถเปลี่ยนแปลงได้ตามเนื้อหาการกำหนดสิ่งเหล่านี้จะซับซ้อนเพราะคุณต้องทำซ้ำ (แต่คุณสามารถแคชได้) แต่กรณีของคุณดูเหมือนจะมีการเปลี่ยนแปลงความสูงสำหรับความกว้างคงที่ หากทราบความกว้าง (เช่นขึ้นอยู่กับความกว้างของหน้าต่างหรือมุมมอง) ก็ไม่มีอะไรซับซ้อน ฉันไม่เข้าใจว่าทำไมความกว้างถึงเปลี่ยนแปลงได้?
Rob Napier

เฉพาะความสูงเท่านั้นที่สามารถเปลี่ยนแปลงได้ตามเนื้อหา และฉันได้แก้ปัญหานั้นแล้ว ฉันมีคลาสย่อย NSTextField ที่ปรับความสูงโดยอัตโนมัติ ปัญหาคือในการรับความรู้เกี่ยวกับการปรับเปลี่ยนนั้นกลับไปยังผู้รับมอบสิทธิ์ของมุมมองตารางเพื่อให้สามารถอัปเดตความสูงได้ตามนั้น
Jiva DeVoe

@Jiva: สงสัยว่าคุณแก้ได้หรือยัง
Joshua Nozzi

ฉันกำลังพยายามทำสิ่งที่คล้ายกัน ฉันชอบแนวคิดของคลาสย่อย NSTextField ที่ปรับความสูงของมันเอง วิธีการเพิ่ม (delegate) การแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงความสูงไปยังคลาสย่อยนั้นและตรวจสอบด้วยคลาสที่เหมาะสม (แหล่งข้อมูล, ร่างผู้ร่วมประชุม, ... ) เพื่อรับข้อมูลในโครงร่าง?
John Velman

คำถามที่เกี่ยวข้องอย่างมากกับคำตอบที่ช่วยให้ฉันมากขึ้นกว่าที่นี่: NSTableView ความสูงของแถวขึ้นอยู่กับ NSStrings
Ashley

คำตอบ:


133

นี่คือปัญหาไก่กับไข่ ตารางจำเป็นต้องทราบความสูงของแถวเนื่องจากเป็นตัวกำหนดว่ามุมมองที่กำหนดจะอยู่ที่ใด แต่คุณต้องการให้มุมมองอยู่รอบ ๆ เพื่อให้คุณสามารถใช้เพื่อหาความสูงของแถว อย่างไหนมาก่อน?

คำตอบคือให้เก็บNSTableCellViewมุมมองพิเศษ(หรือมุมมองใดก็ตามที่คุณใช้เป็น "มุมมองเซลล์") ไว้รอบ ๆ เพื่อวัดความสูงของมุมมอง ในtableView:heightOfRow:วิธีการที่ผู้รับมอบสิทธิ์การเข้าถึงรูปแบบของคุณสำหรับ 'แถว' และการตั้งค่าบนobjectValue NSTableCellViewจากนั้นตั้งค่าความกว้างของมุมมองเป็นความกว้างของตารางของคุณและ (อย่างไรก็ตามคุณต้องการทำ) หาค่าความสูงที่ต้องการสำหรับมุมมองนั้น คืนค่านั้น

อย่าโทรnoteHeightOfRowsWithIndexesChanged:จากด้วยวิธีมอบหมายtableView:heightOfRow:หรือviewForTableColumn:row:! นั่นไม่ดีและจะทำให้เกิดปัญหาใหญ่

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

อย่าลืมว่าภาพnoteHeightOfRowsWithIndexesChanged:เคลื่อนไหวตามค่าเริ่มต้น เพื่อให้ไม่เคลื่อนไหว:

[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
[tableView noteHeightOfRowsWithIndexesChanged:indexSet];
[NSAnimationContext endGrouping];

PS: ฉันตอบคำถามที่โพสต์ใน Apple Dev Forums มากกว่า stack overflow

PSS: ฉันเขียน NSTableView ตามมุมมอง


ขอบคุณสำหรับการตอบกลับ ฉันไม่เคยเห็นมาก่อน ... ฉันทำเครื่องหมายว่าเป็นคำตอบที่ถูกต้อง ฟอรัม dev นั้นยอดเยี่ยมมากฉันก็ใช้มันเช่นกัน
Jiva DeVoe

"หาค่าความสูงที่ต้องการสำหรับมุมมองนั้นส่งคืนค่านั้น" .. นั่นคือปัญหาของฉัน :( ฉันใช้ tableView แบบดูและไม่รู้ว่าจะทำอย่างไร!
Mazyod

@corbin ขอบคุณสำหรับคำตอบนี่คือสิ่งที่ฉันมีในแอพของฉัน อย่างไรก็ตามฉันพบปัญหาเกี่ยวกับ Mavericks เนื่องจากคุณมีความเชี่ยวชาญในเรื่องนี้อย่างชัดเจนคุณช่วยตรวจสอบคำถามของฉันได้ไหม ที่นี่: stackoverflow.com/questions/20924141/…
Alex

1
@corbin มีคำตอบที่ยอมรับได้สำหรับสิ่งนี้ แต่ใช้รูปแบบอัตโนมัติและข้อ จำกัด กับ NSTableViews? มีทางออกที่เป็นไปได้ด้วยการจัดวางอัตโนมัติ สงสัยว่าดีพอหรือไม่หากไม่มี API โดยประมาณ HeightForRowAtIndexPath (ที่ไม่มีใน Cocoa)
ZS

3
@ZS: ฉันใช้ Auto Layout และใช้คำตอบของ Corbin ในtableView:heightOfRow:ผมตั้งค่ามุมมองที่มือถือของฉันมีค่าอะไหล่แถวของ (ฉันมีเพียงหนึ่งคอลัมน์) cellView.fittingSize.heightและผลตอบแทน fittingSizeเป็นวิธี NSView ที่คำนวณขนาดต่ำสุดของมุมมองที่ตรงตามข้อ จำกัด
Peter Hosey

43

สิ่งนี้ง่ายขึ้นมากในmacOS 10.13ด้วย.usesAutomaticRowHeights. ดูรายละเอียดได้ที่นี่: https://developer.apple.com/library/content/releasenotes/AppKit/RN-AppKit/#10_13 (ในหัวข้อ "NSTableView Automatic Row Heights")

โดยทั่วไปคุณเพียงแค่เลือกของคุณNSTableViewหรือNSOutlineViewในตัวแก้ไขกระดานเรื่องราวและเลือกตัวเลือกนี้ในตัวตรวจสอบขนาด:

ใส่คำอธิบายภาพที่นี่

จากนั้นคุณตั้งค่าสิ่งต่างๆในของคุณNSTableCellViewให้มีข้อ จำกัด ด้านบนและด้านล่างของเซลล์จากนั้นเซลล์ของคุณจะปรับขนาดให้พอดีโดยอัตโนมัติ ไม่ต้องใช้รหัส!

แอปของคุณจะไม่สนใจความสูงใด ๆ ที่ระบุไว้ในheightOfRow( NSTableView) และheightOfRowByItem( NSOutlineView) คุณสามารถดูความสูงที่คำนวณได้สำหรับแถวเค้าโครงอัตโนมัติของคุณด้วยวิธีนี้:

func outlineView(_ outlineView: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
  print(rowView.fittingSize.height)
}

@tofutim: หากทำงานได้อย่างถูกต้องกับเซลล์ข้อความที่ห่อไว้หากคุณใช้ข้อ จำกัด ที่ถูกต้องและเมื่อคุณแน่ใจว่าการตั้งค่าความกว้างที่ต้องการของแต่ละ NSTextField ที่มีความสูงแบบไดนามิกจะถูกตั้งค่าเป็นอัตโนมัติ ดูstackoverflow.com/questions/46890144/…
Ely

ฉันคิดว่า @tofutim ถูกต้องสิ่งนี้จะไม่ทำงานหากคุณใส่ NSTextView ลงใน TableView มันพยายามที่จะเก็บถาวร แต่ไม่ได้ผล มันใช้ได้กับส่วนประกอบอื่น ๆ เช่น NSImage ..
Albert

ฉันใช้ความสูงคงที่สำหรับแถว แต่มันไม่ได้ผลสำหรับฉัน
Ken Zira

2
หลังจากไม่กี่วันของการหาสาเหตุที่ไม่ได้ผลสำหรับฉันฉันค้นพบ: มันใช้ได้เฉพาะเมื่อTableCellViewไม่สามารถแก้ไขได้ ไม่ว่าจะถูกตรวจสอบในแอตทริบิวต์ [พฤติกรรม: แก้ไขได้] หรือตัวตรวจสอบการเชื่อมโยง [ตั้งค่าตามเงื่อนไขแก้ไขได้: ใช่]
Łukasz

ฉันแค่ต้องการเน้นว่าการมีข้อ จำกัด ด้านความสูงที่แท้จริงในเซลล์ตารางของคุณมีความสำคัญเพียงใด: ของฉันไม่ได้ดังนั้น fittingSize ที่คำนวณได้ของเซลล์คือ 0.0 และเกิดความไม่พอใจ
green_knight

9

จากคำตอบของ Corbin (ขอบคุณที่ให้ความกระจ่างในเรื่องนี้):

Swift 3, View-Based NSTableView พร้อม Auto-Layout สำหรับ macOS 10.11 (ขึ้นไป)

การตั้งค่าของฉัน: ฉันมีNSTableCellViewที่จัดวางโดยใช้เค้าโครงอัตโนมัติ ประกอบด้วย (นอกเหนือจากองค์ประกอบอื่น ๆ ) หลายบรรทัดNSTextFieldที่มีได้ถึง 2 แถว ดังนั้นความสูงของมุมมองเซลล์ทั้งหมดจึงขึ้นอยู่กับความสูงของช่องข้อความนี้

ฉันอัปเดตบอกให้มุมมองตารางอัปเดตความสูงสองครั้ง:

1) เมื่อปรับขนาดมุมมองตาราง:

func tableViewColumnDidResize(_ notification: Notification) {
        let allIndexes = IndexSet(integersIn: 0..<tableView.numberOfRows)
        tableView.noteHeightOfRows(withIndexesChanged: allIndexes)
}

2) เมื่อวัตถุโมเดลข้อมูลเปลี่ยนแปลง:

tableView.noteHeightOfRows(withIndexesChanged: changedIndexes)

สิ่งนี้จะทำให้มุมมองตารางขอให้ผู้รับมอบสิทธิ์สำหรับความสูงของแถวใหม่

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {

    // Get data object for this row
    let entity = dataChangesController.entities[row]

    // Receive the appropriate cell identifier for your model object
    let cellViewIdentifier = tableCellViewIdentifier(for: entity)

    // We use an implicitly unwrapped optional to crash if we can't create a new cell view
    var cellView: NSTableCellView!

    // Check if we already have a cell view for this identifier
    if let savedView = savedTableCellViews[cellViewIdentifier] {
        cellView = savedView
    }
    // If not, create and cache one
    else if let view = tableView.make(withIdentifier: cellViewIdentifier, owner: nil) as? NSTableCellView {
        savedTableCellViews[cellViewIdentifier] = view
        cellView = view
    }

    // Set data object
    if let entityHandler = cellView as? DataEntityHandler {
        entityHandler.update(with: entity)
    }

    // Layout
    cellView.bounds.size.width = tableView.bounds.size.width
    cellView.needsLayout = true
    cellView.layoutSubtreeIfNeeded()

    let height = cellView.fittingSize.height

    // Make sure we return at least the table view height
    return height > tableView.rowHeight ? height : tableView.rowHeight
}

ขั้นแรกเราต้องได้รับวัตถุโมเดลของเราสำหรับแถว ( entity) และตัวระบุมุมมองเซลล์ที่เหมาะสม จากนั้นตรวจสอบว่าเราได้สร้างมุมมองสำหรับตัวระบุนี้แล้วหรือยัง ในการทำเช่นนั้นเราต้องรักษารายการที่มีมุมมองเซลล์สำหรับตัวระบุแต่ละตัว:

// We need to keep one cell view (per identifier) around
fileprivate var savedTableCellViews = [String : NSTableCellView]()

หากไม่มีการบันทึกเราจำเป็นต้องสร้าง (และแคช) ใหม่ เราอัปเดตมุมมองเซลล์ด้วยออบเจ็กต์แบบจำลองของเราและบอกให้จัดวางใหม่ทุกอย่างตามความกว้างของมุมมองตารางปัจจุบัน fittingSizeสูงนั้นจะสามารถนำมาใช้เป็นความสูงใหม่


นี่คือคำตอบที่สมบูรณ์แบบ หากคุณมีหลายคอลัมน์คุณจะต้องกำหนดขอบเขตเป็นความกว้างของคอลัมน์ไม่ใช่ความกว้างของตาราง
Vojto

นอกจากนี้ตามที่คุณตั้งไว้คุณcellView.bounds.size.width = tableView.bounds.size.widthควรรีเซ็ตความสูงเป็นตัวเลขต่ำ หากคุณวางแผนที่จะใช้มุมมองจำลองนี้ซ้ำการตั้งค่าเป็นตัวเลขต่ำจะ "รีเซ็ต" ขนาดที่เหมาะสม
Vojto

7

สำหรับใครก็ตามที่ต้องการรหัสเพิ่มเติมนี่คือวิธีแก้ปัญหาทั้งหมดที่ฉันใช้ ขอบคุณ corbin dunn ที่ชี้ให้ฉันไปในทิศทางที่ถูกต้อง

ฉันต้องกำหนดความสูงให้สัมพันธ์กับความสูงNSTextViewของฉันNSTableViewCellเป็นส่วนใหญ่

ในคลาสย่อยของNSViewControllerฉันฉันสร้างเซลล์ใหม่ชั่วคราวโดยการโทรoutlineView:viewForTableColumn:item:

- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
    NSTableColumn *tabCol = [[outlineView tableColumns] objectAtIndex:0];
    IBAnnotationTableViewCell *tableViewCell = (IBAnnotationTableViewCell*)[self outlineView:outlineView viewForTableColumn:tabCol item:item];
    float height = [tableViewCell getHeightOfCell];
    return height;
}

- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    IBAnnotationTableViewCell *tableViewCell = [outlineView makeViewWithIdentifier:@"AnnotationTableViewCell" owner:self];
    PDFAnnotation *annotation = (PDFAnnotation *)item;
    [tableViewCell setupWithPDFAnnotation:annotation];
    return tableViewCell;
}

ในของฉันIBAnnotationTableViewCellซึ่งเป็นตัวควบคุมสำหรับมือถือของฉัน (subclass ของNSTableCellView) ฉันมีวิธีการติดตั้ง

-(void)setupWithPDFAnnotation:(PDFAnnotation*)annotation;

ซึ่งตั้งค่าร้านค้าทั้งหมดและตั้งค่าข้อความจาก PDFAnnotations ของฉัน ตอนนี้ฉันสามารถ "คำนวณความสูง" ได้อย่างง่ายดายโดยใช้:

-(float)getHeightOfCell
{
    return [self getHeightOfContentTextView] + 60;
}

-(float)getHeightOfContentTextView
{
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[self.contentTextView font],NSFontAttributeName,nil];
    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[self.contentTextView string] attributes:attributes];
    CGFloat height = [self heightForWidth: [self.contentTextView frame].size.width forString:attributedString];
    return height;
}

.

- (NSSize)sizeForWidth:(float)width height:(float)height forString:(NSAttributedString*)string
{
    NSInteger gNSStringGeometricsTypesetterBehavior = NSTypesetterLatestBehavior ;
    NSSize answer = NSZeroSize ;
    if ([string length] > 0) {
        // Checking for empty string is necessary since Layout Manager will give the nominal
        // height of one line if length is 0.  Our API specifies 0.0 for an empty string.
        NSSize size = NSMakeSize(width, height) ;
        NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:size] ;
        NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:string] ;
        NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init] ;
        [layoutManager addTextContainer:textContainer] ;
        [textStorage addLayoutManager:layoutManager] ;
        [layoutManager setHyphenationFactor:0.0] ;
        if (gNSStringGeometricsTypesetterBehavior != NSTypesetterLatestBehavior) {
            [layoutManager setTypesetterBehavior:gNSStringGeometricsTypesetterBehavior] ;
        }
        // NSLayoutManager is lazy, so we need the following kludge to force layout:
        [layoutManager glyphRangeForTextContainer:textContainer] ;

        answer = [layoutManager usedRectForTextContainer:textContainer].size ;

        // Adjust if there is extra height for the cursor
        NSSize extraLineSize = [layoutManager extraLineFragmentRect].size ;
        if (extraLineSize.height > 0) {
            answer.height -= extraLineSize.height ;
        }

        // In case we changed it above, set typesetterBehavior back
        // to the default value.
        gNSStringGeometricsTypesetterBehavior = NSTypesetterLatestBehavior ;
    }

    return answer ;
}

.

- (float)heightForWidth:(float)width forString:(NSAttributedString*)string
{
    return [self sizeForWidth:width height:FLT_MAX forString:string].height ;
}

heightForWidth: forString: ใช้งานได้ที่ไหน
ZS

ขอโทษสำหรับเรื่องนั้น. เพิ่มเข้าไปในคำตอบ พยายามหาโพสต์ SO ต้นฉบับที่ฉันได้รับจาก แต่ไม่พบ
Sunkas

1
ขอบคุณ! เกือบจะได้ผลสำหรับฉัน แต่มันให้ผลลัพธ์ที่ไม่ถูกต้อง ในกรณีของฉันความกว้างที่ฉันป้อนเข้าไปใน NSTextContainer ดูเหมือนจะผิด คุณใช้เค้าโครงอัตโนมัติเพื่อกำหนดมุมมองย่อยของ NSTableViewCell ของคุณหรือไม่? ความกว้างของ "self.contentTextView" มาจากไหน?
ZS

ฉันโพสต์คำถามเกี่ยวกับปัญหาของฉันที่นี่stackoverflow.com/questions/22929441/…
ZS

3

ฉันกำลังมองหาวิธีแก้ปัญหามาระยะหนึ่งแล้วและหาวิธีแก้ปัญหาต่อไปนี้ซึ่งใช้ได้ดีในกรณีของฉัน:

- (double)tableView:(NSTableView *)tableView heightOfRow:(long)row
{
    if (tableView == self.tableViewTodo)
    {
        CKRecord *record = [self.arrayTodoItemsFiltered objectAtIndex:row];
        NSString *text = record[@"title"];

        double someWidth = self.tableViewTodo.frame.size.width;
        NSFont *font = [NSFont fontWithName:@"Palatino-Roman" size:13.0];
        NSDictionary *attrsDictionary =
        [NSDictionary dictionaryWithObject:font
                                    forKey:NSFontAttributeName];
        NSAttributedString *attrString =
        [[NSAttributedString alloc] initWithString:text
                                        attributes:attrsDictionary];

        NSRect frame = NSMakeRect(0, 0, someWidth, MAXFLOAT);
        NSTextView *tv = [[NSTextView alloc] initWithFrame:frame];
        [[tv textStorage] setAttributedString:attrString];
        [tv setHorizontallyResizable:NO];
        [tv sizeToFit];

        double height = tv.frame.size.height + 20;

        return height;
    }

    else
    {
        return 18;
    }
}

ในกรณีของฉันฉันต้องการขยายคอลัมน์ที่ระบุในแนวตั้งเพื่อให้ข้อความที่ฉันแสดงบนป้ายนั้นพอดีดังนั้นฉันจึงได้รับคอลัมน์ที่มีปัญหาและจับแบบอักษรและความกว้างจากมัน
Klajd Deda

3

ตั้งแต่ผมใช้ที่กำหนดเองNSTableCellViewและฉันมีการเข้าถึงวิธีการแก้ปัญหาของฉันคือการเพิ่มวิธีการในการNSTextFieldNSTextField

@implementation NSTextField (IDDAppKit)

- (CGFloat)heightForWidth:(CGFloat)width {
    CGSize size = NSMakeSize(width, 0);
    NSFont*  font = self.font;
    NSDictionary*  attributesDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
    NSRect bounds = [self.stringValue boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:attributesDictionary];

    return bounds.size.height;
}

@end

ขอบคุณ! คุณคือผู้ช่วยชีวิตของฉัน! นี่เป็นเพียงหนึ่งเดียวที่ทำงานได้อย่างถูกต้อง ฉันใช้เวลาทั้งวันในการคิดออก
Tommy


2

นี่คือสิ่งที่ฉันได้ทำเพื่อแก้ไข:

ที่มา: ดูเอกสาร XCode ใต้ "row height nstableview" คุณจะพบซอร์สโค้ดตัวอย่างชื่อ "TableViewVariableRowHeights / TableViewVariableRowHeightsAppDelegate.m"

(หมายเหตุ: ฉันกำลังดูคอลัมน์ 1 ในมุมมองตารางคุณจะต้องปรับแต่งเพื่อดูที่อื่น)

ใน Delegate.h

IBOutlet NSTableView            *ideaTableView;

ใน Delegate.m

มุมมองตารางมอบหมายการควบคุมความสูงของแถว

    - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
    // Grab the fully prepared cell with our content filled in. Note that in IB the cell's Layout is set to Wraps.
    NSCell *cell = [ideaTableView preparedCellAtColumn:1 row:row];

    // See how tall it naturally would want to be if given a restricted with, but unbound height
    CGFloat theWidth = [[[ideaTableView tableColumns] objectAtIndex:1] width];
    NSRect constrainedBounds = NSMakeRect(0, 0, theWidth, CGFLOAT_MAX);
    NSSize naturalSize = [cell cellSizeForBounds:constrainedBounds];

    // compute and return row height
    CGFloat result;
    // Make sure we have a minimum height -- use the table's set height as the minimum.
    if (naturalSize.height > [ideaTableView rowHeight]) {
        result = naturalSize.height;
    } else {
        result = [ideaTableView rowHeight];
    }
    return result;
}

คุณต้องใช้สิ่งนี้เพื่อให้มีผลกับความสูงของแถวใหม่ (วิธีการมอบหมาย)

- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
    [ideaTableView reloadData];
}

ฉันหวังว่านี่จะช่วยได้.

หมายเหตุสุดท้าย: สิ่งนี้ไม่รองรับการเปลี่ยนความกว้างของคอลัมน์


4
น่าเสียดายที่โค้ดตัวอย่างของ Apple (ซึ่งปัจจุบันทำเครื่องหมายเป็นเวอร์ชัน 1.1) ใช้ตารางแบบเซลล์ไม่ใช่ตารางตามมุมมอง (ซึ่งเป็นคำถามเกี่ยวกับ) การเรียกรหัส-[NSTableView preparedCellAtColumn:row]ซึ่งเอกสารระบุว่า "ใช้ได้เฉพาะกับมุมมองตารางที่ใช้ NSCell" การใช้วิธีนี้บนตารางตามมุมมองจะสร้างเอาต์พุตบันทึกนี้ในขณะรันไทม์: "ดูข้อผิดพลาด NSTableView ที่ใช้: ถูกเรียกใช้ readyCellAtColumn: row: โปรดบันทึกข้อผิดพลาดด้วย backtrace จากบันทึกนี้หรือหยุดใช้เมธอด”
Ashley

1

นี่คือวิธีแก้ปัญหาตามคำตอบของ JanApotheker ซึ่งแก้ไขแล้วเนื่องจากcellView.fittingSize.heightไม่ได้คืนความสูงที่ถูกต้องให้ฉัน ในกรณีของฉันฉันใช้มาตรฐานNSTableCellViewการNSAttributedStringสำหรับข้อความ TextField ของเซลล์และตารางคอลัมน์เดียวที่มีข้อ จำกัด สำหรับเซลล์ของชุด TextField ใน IB

ในตัวควบคุมมุมมองของฉันฉันประกาศ:

var tableViewCellForSizing: NSTableCellView?

ใน viewDidLoad ():

tableViewCellForSizing = tableView.make(withIdentifier: "My Identifier", owner: self) as? NSTableCellView

สุดท้ายสำหรับเมธอด tableView delegate:

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    guard let tableCellView = tableViewCellForSizing else { return minimumCellHeight }

    tableCellView.textField?.attributedStringValue = attributedString[row]
    if let height = tableCellView.textField?.fittingSize.height, height > 0 {
        return height
    }

    return minimumCellHeight
}

mimimumCellHeightคือชุดค่าคงที่เป็น 30 สำหรับการสำรองข้อมูล แต่ไม่เคยใช้จริง attributedStringsคืออาร์เรย์แบบจำลองของฉันเป็นNSAttributedString.

สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับความต้องการของฉัน ขอบคุณสำหรับคำตอบก่อนหน้านี้ทั้งหมดซึ่งชี้ให้ฉันเห็นทิศทางที่ถูกต้องสำหรับปัญหาที่น่ารำคาญนี้


1
ดีมาก. ประเด็นเดียว. ไม่สามารถแก้ไขตารางได้หรือ textfield.fittingSize.height จะไม่ทำงานและจะคืนค่าศูนย์ ตั้งค่า table.isEditable = false ใน viewdidload
jiminybob99

0

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

ทางออกที่ดีที่สุดของฉันคือผลักลอจิกไปที่เซลล์เพราะอย่างน้อยฉันก็สามารถแยกได้ว่าคลาสใดที่จำเป็นในการทำความเข้าใจว่าเซลล์ถูกจัดวางอย่างไร วิธีการเช่น

+ (CGFloat) heightForStory:(Story*) story

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

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