双击编辑NSTableView列标题

时间:2013-01-31 14:04:15

标签: cocoa nstableview

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

我在尝试:

  1. 设置表格视图的双重操作,以便在双击
  2. 时调用自定义方法
  3. 通过调用editWithFrame:inView:editor:delegate:event:来尝试编辑NSTableHeaderCell实例。
  4. 我不完全确定为什么这会扭曲文本,但是当你双击标题时它会使文本看起来像这样,不会出现字段编辑器,

    editWithFrame:inView:editor:delegate:event: on an 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 个答案:

答案 0 :(得分: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

在app delegate的awakeFromNib中不要忘记将NSTableHeaderCell设置为可编辑

- (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之后,我们将编辑器自定义为具有漂亮的白色不透明背景,
这样我们就看不到它下面的文字视图了 至于聚焦环:它是细胞的工作,
我们只是将单元格设置为高亮显示状态,因此它知道它现在必须绘制环

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

现在唯一缺少的是,如果在编辑时调整单元格的大小,字段编辑器的框架将不会更新...

答案 1 :(得分:2)

这也让我感到烦恼,所以我创建了一个示例项目uploaded it to GitHub.

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

双击处理

以下是我们在Swift中已经了解的内容。

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

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

  • TableWindowController(或您的视图控制器)实例作为Nib中的对象,
  • 添加@IBAction func tableViewDoubleClick(sender: NSTableView)或类似的
  • NSTableView的{​​{1}}方法与doubleAction
  • 相关联

编辑单元格非常简单。编辑列标题不是那么多。

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

部分结果:

tableViewDoubleClick

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

正确定位字段编辑器有点工作。我将它放入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) } } 将不会被调用。新值不会被保存。

@ triple.s和@boyfarrell讨论了这个问题,但是没有代码 - 我发现最简单的方法就是知道字段编辑器何时会改变,以便对字段编辑器进行高级构建并手动调用textDidEndEditing

endEditing

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

class HeaderFieldEditor: NSTextView {

    func switchEditingTarget() {

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

        cell.endEditing(self)
    }
}

像魅力一样。

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

答案 2 :(得分:1)

你的问题在这里被否定了回答:Making NSTableView column headers editable

答案 3 :(得分:1)

@BenRhayader - 此解决方案仅在我更改列标题文本并执行选项卡以便调用controlTextDidEndEditing委托时才起作用。但是,如果我更改一列的标题列文本并单击其他列(而不是执行标签),则保留旧文本,即新文本不反映。这可能是因为更改文本的逻辑写在controlTextDidEndEditing中,只有在完成制表符时才会调用。