简体   繁体   English

双击编辑NSTableView列标题

[英]Double-click editing of NSTableView columns headers

Is it possible to change the names of NSTableView columns by double clicking on the columns headers? 是否可以通过双击列标题来更改NSTableView列的名称? Any suggestions on the best way to do this. 有关最佳方法的任何建议。

I am trying: 我在尝试:

  1. Set the double action of the table view to call a custom method on the double click 设置表视图的双重操作以在双击时调用自定义方法
  2. Try and edit the NSTableHeaderCell instance by calling editWithFrame:inView:editor:delegate:event: . 通过调用editWithFrame:inView:editor:delegate:event:尝试编辑NSTableHeaderCell实例editWithFrame:inView:editor:delegate:event: .

I'm not entirely sure why this distorts the text, but when you double click the header it makes the text look like this, no field editor appears, 我不完全确定为什么这会扭曲文本,但是当你双击标题时它会使文本看起来像这样,没有字段编辑器出现,

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

In the AppDelegate, 在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];
    }

}

it seems doable 看起来可行
what you see in your screenshot is the window's field editor overlaying your cell's textfield 您在屏幕截图中看到的是窗口的字段编辑器覆盖您单元格的文本字段
the editor has a transparent background so that's why it's messed up 编辑器有一个透明的背景,这就是为什么它搞砸了

so here's the deal : 所以这是交易:

you will need to have your own NSTableHeaderCell subclass to act as the field editor's delegate : 您将需要拥有自己的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

in the app delegate's awakeFromNib don't forget to set the NSTableHeaderCell to Editable ! 在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:)];
}

for the rest, you were nearly there 其余的,你几乎就在那里
after calling selectWithFrame, we customize the editor to have a nice white opaque background, 调用selectWithFrame之后,我们自定义编辑器以获得漂亮的白色不透明背景,
so that we don't see the textview beneath it 这样我们就看不到它下面的textview了
as for the focus ring : it is the cell's job, 至于焦点圈:这是细胞的工作,
we just set the cell in highlighted state so it knows it has to draw the ring now 我们只是将单元格设置为高亮显示状态,因此它知道它现在必须绘制环

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

some more info about the field editor here : http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/Tasks/UsingWindowFieldEditor.html 有关字段编辑器的更多信息,请访问: http//developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/Tasks/UsingWindowFieldEditor.html

the only thing missing now to make it complete is that the field editor's frame won't update if you resize the cell while editing… 现在唯一缺少的就是如果在编辑时调整单元格的大小,字段编辑器的框架将不会更新...

This bugged me, too, so I created a sample project and uploaded it to GitHub. 这也让我感到烦恼,所以我创建了一个示例项目并将其上传到GitHub。

As @ben-rhayader pointed out, it's all about positioning the field editor above the header view cell. 正如@ ben-rhayader指出的那样,所有关于将字段编辑器定位在标题视图单元格之上。

Double click handling 双击处理

Here's the stuff we already know about in Swift. 这是我们在Swift中已经知道的东西。

Window controller / custom table view controller 窗口控制器/自定义表视图控制器

The interesting part in the view controller is editing the header on double click. 视图控制器中有趣的部分是双击编辑标题。 To make that happen, 为了实现这一目标,

  • put a TableWindowController (or your view controller) instance as an object in your Nib, 将一个TableWindowController (或您的视图控制器)实例作为Nib中的对象,
  • add a @IBAction func tableViewDoubleClick(sender: NSTableView) or similar, 添加@IBAction func tableViewDoubleClick(sender: NSTableView)或类似的,
  • connect the NSTableView 's doubleAction method to tableViewDoubleClick . NSTableViewdoubleAction方法连接到tableViewDoubleClick

Editing cells is straightforward. 编辑单元格很简单。 Editing columns headers not so much. 编辑列标题不是那么多。

  • Header rows have a row value of -1 标题行的行值为-1
  • to position the field editor, you need the column header frame and the field editor itself. 要定位字段编辑器,您需要列标题框架和字段编辑器本身。

Part of the result: 部分结果:

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

Custom header view and header view cells 自定义标题视图和标题视图单元格

Positioning the field editor properly is a bit of work. 正确定位字段编辑器有点工作。 I put it into a NSTableHeaderView subclass: 我把它放到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
    }
}

That takes care of positioning the field editor. 这需要定位字段编辑器。 Now using it from the double click handler from above: 现在从上面的双击处理程序中使用它:

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

How to "end editing" when user double-clicks in another header cell? 当用户在另一个标题单元格中双击时,如何“结束编辑”?

The problem: the field editor is reused and merely re-positioned when a user double-clicks into another header cell. 问题是:当用户双击另一个标题单元格时,字段编辑器会重复使用并仅重新定位。 textDidEndEditing will not be called. textDidEndEditing不会被调用。 The new value will not be saved. 新值不会被保存。

@triple.s and @boyfarrell discussed this but without code -- I find the easiest way to know when the field editor will change is to highjack construction of the field editor and call endEditing manually. @ triple.s和@boyfarrell讨论了这个问题,但是没有代码 - 我发现最简单的方法是知道字段编辑器何时更改是对字段编辑器的endEditing构造和手动调用endEditing

class HeaderFieldEditor: NSTextView {

    func switchEditingTarget() {

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

        cell.endEditing(self)
    }
}

Using this custom field editor when necessary: 必要时使用此自定义字段编辑器:

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

Works like a charm. 奇迹般有效。

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

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

@BenRhayader -- This solution is working only if i change the column header text and do a tab out so that controlTextDidEndEditing delegate is called. @BenRhayader - 此解决方案仅在我更改列标题文本并执行选项卡以便调用controlTextDidEndEditing委托时才起作用。 But if I change the header column text of one column and click on other column(instead of doing a tab out) , the old text is being retained ie the new text is not reflecting. 但是,如果我更改一列的标题列文本并单击其他列(而不是执行制表符),则会保留旧文本,即新文本不会反映。 This may be because the logic to change the text is written inside controlTextDidEndEditing , which is only called when a tab out is done. 这可能是因为更改文本的逻辑写在controlTextDidEndEditing ,只有在完成Tab键时才会调用它。

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

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