9

是否可以通过双击列标题来更改 NSTableView 列的名称?有关执行此操作的最佳方法的任何建议。

我在尝试:

  1. 设置表格视图的双击动作在双击时调用自定义方法
  2. 尝试通过调用编辑 NSTableHeaderCell 实例editWithFrame:inView:editor:delegate:event:

我不完全确定为什么这会扭曲文本,但是当您双击标题时,它会使文本看起来像这样,没有字段编辑器出现,

editWithFrame:inView:editor:delegate:event: 在 NSTableHeaderCell 上

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

}
4

4 回答 4

8


你在截图中看到的似乎是可行

所以这是交易:

您将需要拥有自己的 NSTableHeaderCell 子类来充当字段编辑器的委托:

@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

在应用程序委托的 awakeFromNib 中不要忘记将 NSTableHeaderCell 设置为Editable

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

剩下的,你在调用 selectWithFrame
后就
快到了,我们自定义编辑器有一个漂亮的白色不透明背景, 这样我们就看不到它下面的 textview
和焦点环一样:这是单元格的工作,
我们刚刚设置单元格处于突出显示状态,因此它知道现在必须绘制环

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

有关字段编辑器的更多信息:http: //developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/Tasks/UsingWindowFieldEditor.html

现在唯一要使它完整的是,如果您在编辑时调整单元格的大小,字段编辑器的框架将不会更新……

于 2013-02-13T21:18:21.870 回答
3

这也困扰着我,所以我创建了一个示例项目并将其上传到 GitHub。

正如@ben-rhayader 指出的那样,这完全是关于将字段编辑器定位在标题视图单元格上方。

双击处理

这是我们在 Swift 中已经知道的内容。

窗口控制器/自定义表格视图控制器

视图控制器中有趣的部分是双击编辑标题。为了实现这一点,

  • TableWindowController(或您的视图控制器)实例作为对象放入您的 Nib 中,
  • 添加一个@IBAction func tableViewDoubleClick(sender: NSTableView)或类似的,
  • NSTableView'doubleAction方法连接到tableViewDoubleClick.

编辑单元格很简单。编辑列标题没有那么多。

  • 标题行的行值为 -1
  • 要定位字段编辑器,您需要列标题框架和字段编辑器本身。

部分结果:

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

自定义标题视图和标题视图单元格

正确定位字段编辑器是一项工作。我把它放到一个NSTableHeaderView子类中:

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

这负责定位字段编辑器。现在从上面的双击处理程序中使用它:

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

当用户双击另一个标题单元格时如何“结束编辑”?

问题:字段编辑器被重复使用,并且仅在用户双击另一个标题单元格时重新定位。textDidEndEditing不会被调用。不会保存新值。

@triple.s 和 @boyfarrell 讨论了这个但没有代码——我发现知道字段编辑器何时更改的最简单方法是劫持字段编辑器的构造并endEditing手动调用。

class HeaderFieldEditor: NSTextView {

    func switchEditingTarget() {

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

        cell.endEditing(self)
    }
}

必要时使用此自定义字段编辑器:

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

奇迹般有效。

GitHub 上的项目:https ://github.com/DivineDominion/Editable-NSTableView-Header

于 2016-04-11T15:48:31.220 回答
1

@BenRhayader - 只有当我更改列标题文本并执行制表符以便controlTextDidEndEditing调用委托时,此解决方案才有效。但是,如果我更改一列的标题列文本并单击另一列(而不是执行制表符),旧文本将被保留,即新文本没有反映。这可能是因为更改文本的逻辑写在里面controlTextDidEndEditing,只有在 tab out 完成时才会调用。

于 2013-07-05T05:12:54.417 回答
0

您的问题在这里得到否定的回答:使 NSTableView 列标题可编辑

于 2013-02-12T09:05:08.843 回答