繁体   English   中英

双击编辑NSTableView列标题

[英]Double-click editing of NSTableView columns headers

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

我在尝试:

  1. 设置表视图的双重操作以在双击时调用自定义方法
  2. 通过调用editWithFrame:inView:editor:delegate:event:尝试编辑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];
    }

}

看起来可行
您在屏幕截图中看到的是窗口的字段编辑器覆盖您单元格的文本字段
编辑器有一个透明的背景,这就是为什么它搞砸了

所以这是交易:

您将需要拥有自己的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设置为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

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

这也让我感到烦恼,所以我创建了一个示例项目并将其上传到GitHub。

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

双击处理

这是我们在Swift中已经知道的东西。

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

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

  • 将一个TableWindowController (或您的视图控制器)实例作为Nib中的对象,
  • 添加@IBAction func tableViewDoubleClick(sender: NSTableView)或类似的,
  • NSTableViewdoubleAction方法连接到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构造和手动调用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

你的问题在这里被否定了回答: 使NSTableView列标题可编辑

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM