Frage Doppelklicken Sie auf die Bearbeitung von NSTableView-Spaltenüberschriften


Ist es möglich, die Namen der NSTableView-Spalten durch Doppelklicken auf die Spaltenüberschriften zu ändern? Irgendwelche Vorschläge für den besten Weg, dies zu tun.

Ich versuche:

  1. Legen Sie die Double-Aktion der Tabellenansicht fest, um eine benutzerdefinierte Methode für den Doppelklick aufzurufen
  2. Versuchen Sie, die NSTableHeaderCell-Instanz durch Aufrufen zu bearbeiten editWithFrame:inView:editor:delegate:event:.

Ich bin nicht ganz sicher, warum dies den Text verzerrt, aber wenn Sie auf die Kopfzeile doppelklicken, sieht der Text so aus, es erscheint kein Feld-Editor.

editWithFrame:inView:editor:delegate:event: on an NSTableHeaderCell

Im AppDelegate,

-(void)awakeFromNib
{
    ...
    [_tableView setDoubleAction:@selector(doubleClickInTableView:)];
    ...
}

-(void) doubleClickInTableView:(id)sender
{
    NSInteger row = [_tableView clickedRow];
    NSInteger column = [_tableView clickedColumn];
    if(row == -1){
        /* Want to edit the column header on double-click */
        NSTableColumn *tableColumn = [[_tableView tableColumns] objectAtIndex:column];
        NSTableHeaderView *headerView = [_tableView headerView];
        NSTableHeaderCell *headerCell = [tableColumn headerCell];
        NSRect cellFrame = [headerView headerRectOfColumn:column];
        NSText * fieldEditor = [[headerView window] fieldEditor:YES forObject:nil];
        [headerCell editWithFrame:cellFrame inView:headerView editor:fieldEditor delegate:headerCell event:nil];
    }

}

9
2018-01-31 14:04


Ursprung


Antworten:


es scheint machbar
Was Sie in Ihrem Screenshot sehen, ist der Feld-Editor des Fensters, der das Textfeld Ihrer Zelle überlagert
Der Editor hat einen transparenten Hintergrund, deshalb ist er durcheinander

also hier ist der Deal:

Sie müssen Ihre eigene NSTableHeaderCell-Unterklasse haben, um als Stellvertreter des Feldeditors zu agieren:

@interface NBETableHeaderCell () <NSTextViewDelegate>
@end

@implementation NBETableHeaderCell

- (void)textDidEndEditing:(NSNotification *)notification
{
    NSTextView *editor = notification.object;
    // Update the title, kill the focus ring, end editing
    [self setTitle:editor.string];
    [self setHighlighted:NO];
    [self endEditing:editor];
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
    if([self isHighlighted])
    {
        [self drawFocusRingMaskWithFrame:cellFrame inView:controlView.superview];
    }

    [super drawWithFrame:cellFrame inView:controlView];
}

- (void)drawFocusRingMaskWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
    [controlView lockFocus];
    NSSetFocusRingStyle(NSFocusRingOnly);
    [[NSBezierPath bezierPathWithRect:cellFrame] fill];
    [controlView unlockFocus];
}

@end

Vergessen Sie nicht, in der watchFromNib des Anwendungsdelegaten die NSTableHeaderCell auf zu setzen Bearbeitbar !

- (void)awakeFromNib
{
    NSTableColumn *newCol = [[NSTableColumn alloc] initWithIdentifier:@"whatever"];

    NBETableHeaderCell *hc = [[NBETableHeaderCell alloc] initTextCell:@"Default header text"];
    [hc setEditable:YES];
    [hc setUsesSingleLineMode:YES];
    [hc setScrollable:NO];
    [hc setLineBreakMode:NSLineBreakByTruncatingTail];
    [newCol setHeaderCell:hc];

    [self.tableView addTableColumn:newCol];
    [self.tableView setDoubleAction:@selector(doubleClickInTableView:)];
}

für den Rest waren Sie fast da
nach Wenn wir selectWithFrame aufrufen, passen wir den Editor so an, dass er einen schönen weißen undurchsichtigen Hintergrund hat.
damit wir die Textansicht darunter nicht sehen
wie für den Fokusring: es ist die Aufgabe der Zelle,
Wir setzen die Zelle einfach in den markierten Zustand, damit sie weiß, dass sie jetzt den Ring zeichnen muss

- (void)doubleClickInTableView:(id)sender
{
    NSInteger row = [_tableView clickedRow];
    NSInteger column = [_tableView clickedColumn];

    if(row == -1&& column >= 0)
    {
        NSTableColumn *tableColumn = [[_tableView tableColumns] objectAtIndex:column];
        NSTableHeaderView *headerView = [_tableView headerView];
        NBETableHeaderCell *headerCell = [tableColumn headerCell];

        // cellEditor is basically a unique NSTextView shared by the window
        // that adjusts its style to the field calling him
        // it stands above the text field's view giving the illusion that you are editing it
        // and if it has no background you will see the editor's NSTextView overlaying the TextField
        // wich is why you have that nasty bold text effect in your screenshot
        id cellEditor = [self.window fieldEditor:YES forObject:self.tableView];

        [headerCell setHighlighted:YES];
        [headerCell selectWithFrame:[headerView headerRectOfColumn:column]
                             inView:headerView
                             editor:cellEditor
                           delegate:headerCell
                              start:0
                             length:headerCell.stringValue.length];

        [cellEditor setBackgroundColor:[NSColor whiteColor]];
        [cellEditor setDrawsBackground:YES];
    }
}

Hier noch ein paar Infos zum Feld-Editor: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/Tasks/UsingWindowFieldEditor.html 

Das einzige, was jetzt noch fehlt, um es vollständig zu machen, ist, dass der Rahmen des Feldeditors nicht aktualisiert wird, wenn Sie die Größe der Zelle während der Bearbeitung ändern ...


8
2018-02-13 21:18



Das hat mich auch gestört, also habe ich ein Beispielprojekt erstellt und es auf GitHub hochgeladen.

Wie @ ben-rhayader hervorhebt, geht es darum, den Feldeditor über der Header-View-Zelle zu positionieren.

Doppelklick-Handhabung

Hier sind die Sachen, von denen wir bereits in Swift wissen.

Fenster Controller / benutzerdefinierte Tabellenansicht Controller

Der interessante Teil im View-Controller ist das Bearbeiten der Kopfzeile bei Doppelklick. Um das zu ermöglichen,

  • a TableWindowController (oder Ihre View-Controller) Instanz als ein Objekt in Ihrem Nib,
  • füge hinzu ein @IBAction func tableViewDoubleClick(sender: NSTableView) o.ä,
  • verbinde die NSTableViewist es doubleAction Methode zu tableViewDoubleClick.

Bearbeiten von Zellen ist einfach. Spaltenköpfe nicht so sehr bearbeiten.

  • Kopfzeilen haben einen Zeilenwert von -1
  • Um den Feldeditor zu positionieren, benötigen Sie den Spaltenkopf und den Feldeditor.

Teil des Ergebnisses:

extension TableWindowController {

    @IBAction func tableViewDoubleClick(sender: NSTableView) {

        let column = sender.clickedColumn
        let row = sender.clickedRow

        guard column > -1 else { return }

        if row == -1 {
            editColumnHeader(tableView: sender, column: column)
            return
        }

        editCell(tableView: sender, column: column, row: row)
    }

    private func editColumnHeader(tableView tableView: NSTableView, column: Int) {

        guard column > -1,
            let tableColumn = tableView.tableColumn(column: column),
            headerView = tableView.headerView as? TableHeaderView,
            headerCell = tableColumn.headerCell as? TableHeaderCell,
            fieldEditor = fieldEditor(object: headerView)
            else { return }

        headerCell.edit(
            fieldEditor: fieldEditor,
            frame: headerView.paddedHeaderRect(column: column),
            headerView: headerView)
    }

    private func editCell(tableView tableView: NSTableView, column: Int, row: Int) {

        guard row > -1 && column > -1,
            let view = tableView.viewAtColumn(column, row: row, makeIfNecessary: true) as? NSTableCellView
            else { return }

        view.textField?.selectText(self)
    }

    /// Convenience accessor to the `window`s field editor.
    func fieldEditor(object object: AnyObject?) -> NSText? {
        return self.window?.fieldEditor(true, forObject: object)
    }
}

Benutzerdefinierte Kopfzeilen- und Kopfzeilenansichtszellen

Den Feld-Editor richtig zu positionieren ist ein wenig Arbeit. Ich habe es in ein NSTableHeaderView Unterklasse:

class TableHeaderView: NSTableHeaderView {

    /// Trial and error result of the text frame that fits.
    struct Padding {
        static let Vertical: CGFloat = 4
        static let Right: CGFloat = 1
    }

    /// By default, the field editor will be very high and thus look weird.
    /// This scales the header rect down a bit so the field editor is put
    /// truly in place.
    func paddedHeaderRect(column column: Int) -> NSRect {

        let paddedVertical = CGRectInset(self.headerRectOfColumn(column), 0, Padding.Vertical)
        let paddedRight = CGRect(
            origin: paddedVertical.origin,
            size: CGSize(width: paddedVertical.width - Padding.Right, height: paddedVertical.height))

        return paddedRight
    }
}

Das sorgt für die Positionierung des Feldeditors. Jetzt verwenden es aus dem Doppelklick-Handler von oben:

class TableHeaderCell: NSTableHeaderCell, NSTextViewDelegate {

    func edit(fieldEditor fieldEditor: NSText, frame: NSRect, headerView: NSView) {

        let endOfText = (self.stringValue as NSString).length
        self.highlighted = true
        self.selectWithFrame(frame,
            inView: headerView,
            editor: fieldEditor,
            delegate: self,
            start: endOfText,
            length: 0)

        fieldEditor.backgroundColor = NSColor.whiteColor()
        fieldEditor.drawsBackground = true
    }

    func textDidEndEditing(notification: NSNotification) {

        guard let editor = notification.object as? NSText else { return }

        self.title = editor.string ?? ""
        self.highlighted = false
        self.endEditing(editor)
    }
}

Wie kann man die Bearbeitung beenden, wenn der Benutzer in eine andere Kopfzeile doppelklickt?

Das Problem: Der Feldeditor wird wiederverwendet und lediglich neu positioniert, wenn ein Benutzer in eine andere Header-Zelle doppelklickt. textDidEndEditingwird nicht angerufen. Der neue Wert wird nicht gespeichert.

@ triple.s und @boyfarrell haben das besprochen, aber ohne Code - Ich finde den einfachsten Weg zu wissen, wann sich der Feld-Editor ändert, ist, den Aufbau des Feld-Editors zu kopieren und aufzurufen endEditing manuell.

class HeaderFieldEditor: NSTextView {

    func switchEditingTarget() {

        guard let cell = self.delegate as? NSCell else { return }

        cell.endEditing(self)
    }
}

Verwenden Sie diesen benutzerdefinierten Feld-Editor bei Bedarf:

class TableWindowController: NSWindowDelegate {

    func windowWillReturnFieldEditor(sender: NSWindow, toObject client: AnyObject?) -> AnyObject? {

        // Return default field editor for everything not in the header.
        guard client is TableHeaderView else { return nil }

        // Comment out this line to see what happens by default: the old header
        // is not deselected.
        headerFieldEditor.switchEditingTarget()

        return headerFieldEditor
    }

    lazy var headerFieldEditor: HeaderFieldEditor = {
        let editor = HeaderFieldEditor()
        editor.fieldEditor = true
        return editor
    }()
}

Klappt wunderbar.

Projekt auf GitHub: https://github.com/DivineDominion/Editable-NSTableView-Header


2
2018-04-11 15:48



Deine Frage wurde hier negativ beantwortet: NSTableView-Spaltenüberschriften editierbar machen


1
2018-02-12 09:05



@BenRhayader - Diese Lösung funktioniert nur, wenn ich den Spaltenüberschriftstext ändere und eine Registerkarte heraus führe, so dass controlTextDidEndEditing Delegat wird aufgerufen. Wenn ich jedoch den Spaltentitel der Kopfzeile einer Spalte ändere und auf eine andere Spalte klicke (anstatt einen Tabulator zu erstellen), wird der alte Text beibehalten, d. H. Der neue Text wird nicht reflektiert. Dies kann daran liegen, dass die Logik zum Ändern des Textes darin geschrieben ist controlTextDidEndEditing, die nur aufgerufen wird, wenn ein Tabulator ausgeführt wird.


1
2017-07-05 05:12