Ansichtsbasiertes NSTableView mit Zeilen mit dynamischen Höhen

84

Ich habe eine Anwendung mit einer Ansicht, die darauf basiert NSTableView. In dieser Tabellenansicht befinden sich Zeilen mit Zellen, deren Inhalt aus mehreren Zeilen NSTextFieldmit aktiviertem Zeilenumbruch besteht. Abhängig vom Textinhalt von NSTextFieldvariiert die Größe der Zeilen, die zum Anzeigen der Zelle benötigt werden.

Ich weiß, dass ich die NSTableViewDelegateMethode implementieren kann - tableView:heightOfRow:um die Höhe zurückzugeben, aber die Höhe wird basierend auf dem Wortumbruch bestimmt, der auf dem verwendet wird NSTextField. Die Wortumhüllung von NSTextFieldbasiert in ähnlicher Weise darauf, wie breit das NSTextFieldist ... was durch die Breite von bestimmt wird NSTableView.

Soooo… ich denke meine Frage ist… was ist ein gutes Designmuster dafür? Es scheint, als wäre alles, was ich versuche, ein kompliziertes Durcheinander. Da die TableView Kenntnisse über die Höhe der Zellen erfordert, um sie auszurichten ... und die NSTextFieldKenntnisse über das Layout, um den Zeilenumbruch zu bestimmen ... und die Zelle Kenntnisse über den Wortumbruch benötigt, um die Höhe zu bestimmen ... ist es ein kreisförmiges Durcheinander ... und es macht mich wahnsinnig .

Vorschläge?

Wenn es darauf ankommt, kann das Endergebnis auch bearbeitet werden NSTextFields, um die Größe an den darin enthaltenen Text anzupassen. Ich habe dies bereits auf der Ansichtsebene ausgeführt, aber die Tabellenansicht passt die Höhe der Zellen noch nicht an. Ich denke, sobald ich das Höhenproblem gelöst habe, werde ich das -noteHeightOfRowsWithIndexesChanged -Methode verwenden, um die Tabellenansicht über die geänderte Höhe zu informieren ... aber dann wird der Delegierte immer noch nach der Höhe gefragt ... daher mein Dilemma.

Danke im Voraus!

Jiva DeVoe
quelle
Wenn sich sowohl die Breite als auch die Höhe der Ansicht mit ihrem Inhalt ändern können, ist die Definition kompliziert, da Sie iterieren müssen (aber dann können Sie zwischenspeichern). Ihr Fall scheint jedoch eine sich ändernde Höhe bei konstanter Breite zu haben. Wenn die Breite bekannt ist (dh basierend auf der Breite des Fensters oder der Ansicht), ist hier nichts kompliziert. Mir fehlt, warum die Breite variabel ist?
Rob Napier
Nur die Höhe kann sich mit dem Inhalt ändern. Und dieses Problem habe ich bereits gelöst. Ich habe eine NSTextField-Unterklasse, die die Höhe automatisch anpasst. Das Problem besteht darin, dem Delegaten der Tabellenansicht wieder Kenntnis von dieser Anpassung zu verschaffen, damit er die Höhe entsprechend aktualisieren kann.
Jiva DeVoe
@ Jiva: Ich frage mich, ob du das schon gelöst hast.
Joshua Nozzi
Ich versuche etwas Ähnliches zu tun. Ich mag die Idee einer NSTextField-Unterklasse, die ihre eigene Höhe anpasst. Wie wäre es, wenn Sie dieser Unterklasse eine (delegierte) Benachrichtigung über Höhenänderungen hinzufügen und diese mit einer geeigneten Klasse (Datenquelle, Gliederungsdelegierter, ...) überwachen, um Informationen zur Gliederung zu erhalten?
John Velman
Eine sehr verwandte Frage mit Antworten, die mir mehr geholfen haben als die hier: NSTableView-Zeilenhöhe basierend auf NSStrings .
Ashley

Antworten:

131

Dies ist ein Henne-Ei-Problem. Die Tabelle muss die Zeilenhöhe kennen, da dies bestimmt, wo eine bestimmte Ansicht liegen wird. Sie möchten jedoch, dass eine Ansicht bereits vorhanden ist, damit Sie die Zeilenhöhe ermitteln können. Also, was kommt zuerst?

Die Antwort besteht darin, eine zusätzliche NSTableCellViewAnsicht (oder eine beliebige Ansicht, die Sie als "Zellenansicht" verwenden) beizubehalten, nur um die Höhe der Ansicht zu messen. Greifen Sie in der tableView:heightOfRow:Delegate-Methode auf Ihr Modell für 'row' zu und setzen Sie die Option objectValueon NSTableCellView. Stellen Sie dann die Breite der Ansicht auf die Breite Ihrer Tabelle ein und ermitteln Sie (wie auch immer Sie dies tun möchten) die erforderliche Höhe für diese Ansicht. Geben Sie diesen Wert zurück.

Rufen Sie nicht noteHeightOfRowsWithIndexesChanged:in der Delegate-Methode tableView:heightOfRow:oder auf viewForTableColumn:row:! Das ist schlecht und wird Mega-Ärger verursachen.

Um die Höhe dynamisch zu aktualisieren, müssen Sie auf die Textänderung (über das Ziel / die Aktion) reagieren und die berechnete Höhe dieser Ansicht neu berechnen. Ändern Sie jetzt nicht dynamisch die NSTableCellViewHöhe des Objekts (oder die Ansicht, die Sie als "Zellenansicht" verwenden). Die Tabelle muss den Rahmen dieser Ansicht steuern, und Sie kämpfen gegen die Tabellenansicht, wenn Sie versuchen, sie festzulegen . Rufen Sie stattdessen in Ihrem Ziel / Ihrer Aktion für das Textfeld, in dem Sie die Höhe berechnet haben noteHeightOfRowsWithIndexesChanged:, die Größe der einzelnen Zeile für die Tabelle auf. Angenommen, Sie haben Ihr Autoresizing-Masken-Setup direkt in Unteransichten (dh Unteransichten von NSTableCellView), sollte die Größe der Dinge in Ordnung sein! Wenn nicht, arbeiten Sie zuerst an der Größenänderungsmaske der Unteransichten, um die Dinge mit variablen Zeilenhöhen richtig zu machen.

Vergessen Sie nicht, dass dies noteHeightOfRowsWithIndexesChanged:standardmäßig animiert wird. Um es nicht zu animieren:

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

PS: Ich beantworte mehr Fragen in den Apple Dev-Foren als Stapelüberlauf.

PSS: Ich habe die ansichtsbasierte NSTableView geschrieben

Corbin Dunn
quelle
Danke für die Antwort. Ich hatte es ursprünglich nicht gesehen ... Ich habe es als die richtige Antwort markiert. Die Entwicklerforen sind großartig, ich benutze sie auch.
Jiva DeVoe
"Ermitteln Sie die erforderliche Höhe für diese Ansicht. Geben Sie diesen Wert zurück." .. Das ist mein Problem :(. Ich verwende view-based tableView, und ich habe keine Ahnung, wie das geht!
Mazyod
@corbin Danke für die Antwort, das hatte ich auf meiner App. Ich fand jedoch ein Problem mit Mavericks. Würde es Ihnen etwas ausmachen, meine Frage zu prüfen, da Sie sich mit diesem Thema gut auskennen? Hier: stackoverflow.com/questions/20924141/…
Alex
1
@corbin gibt es eine kanonische Antwort darauf, aber mit automatischem Layout und Einschränkungen mit NSTableViews? Es gibt eine mögliche Lösung mit automatischem Layout. Ich frage mich, ob das ohne die geschätzten HeightForRowAtIndexPath-APIs (die nicht in Cocoa enthalten sind) gut genug ist
ZS
3
@ZS: Ich verwende Auto Layout und habe Corbins Antwort implementiert. In tableView:heightOfRow:richte ich meine Ersatzzellenansicht mit den Zeilenwerten ein (ich habe nur eine Spalte) und kehre zurück cellView.fittingSize.height. fittingSizeist eine NSView-Methode, die die minimale Größe der Ansicht berechnet, die ihre Einschränkungen erfüllt.
Peter Hosey
41

Dies wurde in macOS 10.13 mit viel einfacher .usesAutomaticRowHeights. Die Details finden Sie hier: https://developer.apple.com/library/content/releasenotes/AppKit/RN-AppKit/#10_13 (Im Abschnitt "NSTableView Automatic Row Heights").

Grundsätzlich wählen Sie einfach Ihr NSTableViewoder NSOutlineViewim Storyboard-Editor aus und wählen diese Option im Größeninspektor aus:

Geben Sie hier die Bildbeschreibung ein

Dann stellen Sie das Material in Ihrer so ein NSTableCellView, dass die Zelle über obere und untere Einschränkungen verfügt, und die Größe Ihrer Zelle wird automatisch angepasst. Kein Code erforderlich!

Ihre App ignoriert alle in heightOfRow( NSTableView) und heightOfRowByItem( NSOutlineView) angegebenen Höhen . Mit dieser Methode können Sie sehen, welche Höhen für Ihre automatischen Layoutzeilen berechnet werden:

func outlineView(_ outlineView: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
  print(rowView.fittingSize.height)
}
Clifton Labrum
quelle
@tofutim: Wenn es mit umgebrochenen Textzellen korrekt funktioniert, wenn Sie die richtigen Einschränkungen verwenden und sicherstellen, dass die Einstellung Bevorzugte Breite jedes NSTextField mit dynamischer Höhe auf Automatisch festgelegt ist. Siehe auch stackoverflow.com/questions/46890144/…
Ely
Ich denke, @tofutim ist richtig, dies wird nicht funktionieren, wenn Sie NSTextView in TableView einfügen, es versucht, es zu archivieren, aber es funktioniert nicht. Es funktioniert jedoch für andere Komponenten, zum Beispiel NSImage.
Albert
Ich habe eine feste Höhe für eine Reihe verwendet, aber es funktioniert nicht für mich
Ken Zira
1
Nachdem ich einige Tage herausgefunden hatte, warum es bei mir nicht funktioniert, stellte ich fest: Es funktioniert NUR, wenn TableCellViewes NICHT bearbeitet werden kann. Egal, wird in Attributen [Verhalten: Bearbeitbar] oder Bindungsinspektor [Bedingt bearbeitbar: Ja]
Łukasz
Ich möchte nur betonen, wie wichtig es ist, tatsächliche Höhenbeschränkungen in Ihrer Tabellenzelle zu haben: meine nicht, daher betrug die berechnete Anpassungsgröße der Zelle 0,0, und es kam zu Frustration.
green_knight
9

Basierend auf Corbins Antwort (übrigens danke, dass wir etwas Licht ins Dunkel bringen):

Swift 3, View-Based NSTableView mit Auto-Layout für macOS 10.11 (und höher)

Mein Setup: Ich habe ein NSTableCellView, das mit Auto-Layout angelegt ist. Es enthält (neben anderen Elementen) eine mehrzeilige Zeile NSTextFieldmit bis zu 2 Zeilen. Daher hängt die Höhe der gesamten Zellenansicht von der Höhe dieses Textfelds ab.

Ich aktualisiere die Tabellenansicht, um die Höhe zweimal zu aktualisieren:

1) Wenn die Größe der Tabellenansicht geändert wird:

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

2) Wenn sich das Datenmodellobjekt ändert:

tableView.noteHeightOfRows(withIndexesChanged: changedIndexes)

Dadurch fragt die Tabellenansicht den Delegierten nach der neuen Zeilenhöhe.

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
}

Zuerst müssen wir unser Modellobjekt für die Zeile ( entity) und den entsprechenden Bezeichner für die Zellenansicht abrufen. Wir prüfen dann, ob wir bereits eine Ansicht für diesen Bezeichner erstellt haben. Dazu müssen wir für jeden Bezeichner eine Liste mit Zellenansichten führen:

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

Wenn keine gespeichert ist, müssen wir eine neue erstellen (und zwischenspeichern). Wir aktualisieren die Zellenansicht mit unserem Modellobjekt und weisen es an, alles basierend auf der aktuellen Breite der Tabellenansicht neu zu gestalten. Die fittingSizeHöhe kann dann als neue Höhe verwendet werden.

JanApotheker
quelle
Dies ist die perfekte Antwort. Wenn Sie mehrere Spalten haben, möchten Sie die Grenzen auf die Spaltenbreite und nicht auf die Tabellenbreite festlegen.
Vojto
Außerdem ist es beim Einstellen cellView.bounds.size.width = tableView.bounds.size.widtheine gute Idee, die Höhe auf eine niedrige Zahl zurückzusetzen. Wenn Sie diese Dummy-Ansicht wiederverwenden möchten, wird die Anpassungsgröße durch Zurücksetzen auf eine niedrige Zahl "zurückgesetzt".
Vojto
7

Für alle, die mehr Code wünschen, ist hier die vollständige Lösung, die ich verwendet habe. Danke corbin dunn, dass du mich in die richtige Richtung gelenkt hast.

Ich musste die Höhe hauptsächlich in Bezug darauf einstellen, wie hoch ein NSTextViewin meinem NSTableViewCellwar.

In meiner Unterklasse von NSViewControllererstelle ich vorübergehend eine neue Zelle, indem ich aufrufeoutlineView: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;
}

In meinem IBAnnotationTableViewCell, dem Controller für meine Zelle (Unterklasse von NSTableCellView), habe ich eine Setup-Methode

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

Hiermit werden alle Steckdosen eingerichtet und der Text aus meinen PDFAnnotations festgelegt. Jetzt kann ich die Höhe "einfach" berechnen mit:

-(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 ;
}
Sunkas
quelle
Wo ist heightForWidth: forString: implementiert?
ZS
Das tut mir leid. Fügte es der Antwort hinzu. Ich habe versucht, den ursprünglichen SO-Beitrag zu finden, von dem ich diesen erhalten habe, konnte ihn aber nicht finden.
Sunkas
1
Vielen Dank! Es funktioniert fast für mich, aber es gibt mir ungenaue Ergebnisse. In meinem Fall scheint die Breite, die ich in den NSTextContainer einspeise, falsch zu sein. Verwenden Sie das automatische Layout, um die Unteransichten Ihrer NSTableViewCell zu definieren? Woher kommt die Breite von "self.contentTextView"?
ZS
Ich habe hier eine Frage zu meinem Problem gestellt: stackoverflow.com/questions/22929441/…
ZS
3

Ich habe lange nach einer Lösung gesucht und mir die folgende ausgedacht, die in meinem Fall hervorragend funktioniert:

- (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;
    }
}
vomako
quelle
In meinem Fall wollte ich eine bestimmte Spalte vertikal erweitern, damit der auf diesem Etikett angezeigte Text all-in passt. Also erhalte ich die betreffende Spalte und nehme die Schriftart und die Breite daraus.
Klajd Deda
3

Da ich benutzerdefinierte verwende NSTableCellViewund Zugriff auf die habe, bestand NSTextFieldmeine Lösung darin, eine Methode hinzuzufügen NSTextField.

@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
Klajd Deda
quelle
Vielen Dank! Du bist mein Retter! Dies ist die einzige, die richtig funktioniert. Ich habe einen ganzen Tag damit verbracht, das herauszufinden.
Tommy
2

Folgendes habe ich getan, um das Problem zu beheben:

Quelle: Schauen Sie in die XCode-Dokumentation unter "Zeilenhöhe nstableview". Sie finden einen Beispielquellcode mit dem Namen "TableViewVariableRowHeights / TableViewVariableRowHeightsAppDelegate.m".

(Hinweis: Ich betrachte Spalte 1 in der Tabellenansicht. Sie müssen Änderungen vornehmen, um woanders zu suchen.)

in Delegate.h

IBOutlet NSTableView            *ideaTableView;

in Delegate.m

Die Tabellenansicht delegiert die Steuerung der Zeilenhöhe

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

Sie benötigen dies auch, um die neue Zeilenhöhe zu bewirken (delegierte Methode).

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

Ich hoffe das hilft.

Schlussbemerkung: Dies unterstützt keine Änderung der Spaltenbreite.

Laurent
quelle
Du rockst! :) Danke.
Flagman
4
Leider verwendet dieser Apple-Beispielcode (der derzeit als Version 1.1 gekennzeichnet ist) eine zellbasierte Tabelle, keine ansichtsbasierte Tabelle (um die es in dieser Frage geht). Die -[NSTableView preparedCellAtColumn:row]Codeaufrufe, die laut den Dokumenten "nur für NSCell-basierte Tabellenansichten verfügbar sind". Die Verwendung dieser Methode in einer ansichtsbasierten Tabelle erzeugt diese Protokollausgabe zur Laufzeit: "Ansichtsbasierter NSTableView-Fehler: prepareCellAtColumn: row: wurde aufgerufen. Bitte protokollieren Sie einen Fehler mit der Rückverfolgung aus diesem Protokoll oder beenden Sie die Verwendung der Methode."
Ashley
1

Hier ist eine Lösung, die auf der Antwort von JanApotheker basiert und geändert wurde, da cellView.fittingSize.heightfür mich nicht die richtige Höhe zurückgegeben wurde. In meinem Fall verwende ich den Standard NSTableCellView, einen NSAttributedStringfür den TextField-Text der Zelle und eine einzelne Spaltentabelle mit Einschränkungen für das in IB festgelegte TextField der Zelle.

In meinem View Controller erkläre ich:

var tableViewCellForSizing: NSTableCellView?

In viewDidLoad ():

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

Schließlich für die tableView-Delegatenmethode:

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
}

mimimumCellHeightist eine Konstante, die für die Sicherung auf 30 gesetzt ist, aber nie verwendet wird. attributedStringsist mein Modellarray von NSAttributedString.

Dies funktioniert perfekt für meine Bedürfnisse. Vielen Dank für alle vorherigen Antworten, die mich für dieses lästige Problem in die richtige Richtung gelenkt haben.

jbaraga
quelle
Sehr gut. Ein Problem. Die Tabelle kann nicht bearbeitet werden oder textfield.fittingSize.height funktioniert nicht und gibt Null zurück. Setze table.isEditable = false in viewdidload
jiminybob99
0

Das klingt sehr nach etwas, das ich vorher tun musste. Ich wünschte, ich könnte Ihnen sagen, dass ich eine einfache, elegante Lösung gefunden habe, aber leider nicht. Nicht aus Mangel an Versuchen. Wie Sie bereits bemerkt haben, lässt die Notwendigkeit von UITableView, die Höhe vor dem Erstellen der Zellen zu kennen, alles ziemlich kreisförmig erscheinen.

Meine beste Lösung bestand darin, die Logik in die Zelle zu übertragen, da ich zumindest isolieren konnte, welche Klasse benötigt wurde, um zu verstehen, wie die Zellen angeordnet waren. Eine Methode wie

+ (CGFloat) heightForStory:(Story*) story

würde bestimmen können, wie groß die Zelle sein musste. Dazu gehörte natürlich das Messen von Text usw. In einigen Fällen habe ich Methoden entwickelt, um Informationen, die mit dieser Methode gewonnen wurden, zwischenzuspeichern, die dann beim Erstellen der Zelle verwendet werden konnten. Das war das Beste, was ich mir ausgedacht habe. Es ist jedoch ein ärgerliches Problem, da es anscheinend eine bessere Antwort geben sollte.

Landstreicher
quelle