简体   繁体   English

基于视图的NSTableView编辑

[英]View based NSTableView editing

I'm not sure what I'm doing wrong. 我不确定我做错了什么。 Since I couldn't find any other questions (or even documentation) about this, it seems do be normally working without problems for other people. 由于我找不到任何关于此的任何其他问题(甚至文档),似乎通常对其他人没有问题。

I'm simply trying to get a view based NSTableView to do support editing of it's content. 我只是想尝试使用基于NSTableView的视图来支持编辑它的内容。 Ie the app displays a NSTableView with one column and several rows, containing a NSTextField with some content. 即应用程序显示带有一列和多行的NSTableView,其中包含带有一些内容的NSTextField。 I want to be able to (double) click on a cell and edit the cell's content. 我希望能够(双)单击一个单元格并编辑单元格的内容。 So basically the normal behavior of a cell based NSTableView where the tableView:setObjectValue:forTableColumn:row: method is implemented. 所以基本上是基于NSTableView的单元格的正常行为,其中实现了tableView:setObjectValue:forTableColumn:row:方法。

I analyzed the Complex TableView example in the TableViewPlayground sample code from Apple (which is supporting editing of cell content), but I cannot find the setting/code/switch which is enabling the editing. 我分析了Apple的TableViewPlayground示例代码中的Complex TableView示例(支持编辑单元格内容),但我找不到启用编辑的设置/代码/开关。

Here's a simple sample project (Xcode 6.1.1, SDK 10.10, storyboard based): 这是一个简单的示例项目(Xcode 6.1.1,SDK 10.10,基于故事板):

Header: 标题:

#import <Cocoa/Cocoa.h>

@interface ViewController : NSViewController

@property (weak) IBOutlet NSTableView *tableView;

@end

Implementation: 执行:

#import "ViewController.h"

@implementation ViewController
{
    NSMutableArray* _content;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    _content = [NSMutableArray array];

    for(NSInteger i = 0; i<10; i++) {
        [_content addObject:[NSString stringWithFormat:@"Item %ld", i]];
    }
}

#pragma mark - NSTableViewDataSource
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return _content.count;
}

#pragma mark - NSTableViewDelegate
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    NSTableCellView* cell = [tableView makeViewWithIdentifier:@"CellView" owner:self];
    cell.textField.stringValue = _content[row];
    return cell;
}

- (IBAction)endEditingText:(id)sender {
    NSInteger row = [_tableView rowForView:sender];
    if (row != -1) {
        _content[row] = [sender stringValue];
    }
}
@end

The storyboard file looks like this: 故事板文件如下所示: 故事板

The datasource and delegate of the table view are set to the view controller. 表视图的数据源和委托设置为视图控制器。 When running this app, the table view displays the 10 test rows, but it is not possible to edit one of the rows. 运行此应用程序时,表视图显示10个测试行,但无法编辑其中一行。

Why is that? 这是为什么? What did I miss here? 我在这里想念的是什么?

I double checked all attributes of the NSTableView (and it's contents) to be the same as in the TableViewPlayground sample from Apple. 我仔细检查了NSTableView的所有属性(及其内容)与Apple的TableViewPlayground示例中的相同。 And after several hours of searching the documentation and internet for helpful hints without any success, I'm kind of frustrated. 经过几个小时的搜索文档和互联网有用的提示没有任何成功,我有点沮丧。 All you can find on view based NSTableViews are non-editable samples or very vague information on editable content. 您在基于视图的NSTableViews上找到的所有内容都是不可编辑的样本,或者是关于可编辑内容的非常模糊的信息。 And of course, there is tons of information, documentation and samples on editable, cell based NSTableViews... 当然,在可编辑的基于单元格的NSTableViews上有大量的信息,文档和示例......

A zip with my sample project is downloadable here: TableTest.zip 我的示例项目的zip可以在这里下载: TableTest.zip

Even though all the pieces for editing a view based NSTableView are present in the question and answer, I still had trouble putting it all together. 即使编辑基于NSTableView的视图的所有部分都存在于问题和答案中,我仍然无法将它们整合在一起。 The following demo is in Swift, using Xcode 6.3.2, but it should be easy to follow for the objective-C cavemen/womens. 以下演示使用Xcode 6.3.2在Swift中进行,但对于Objective-C cavemen / womens应该很容易理解。 A full code listing is at the end. 最后是一个完整的代码清单。

Let's start here: 我们从这里开始:

NSTableViewDataSource Protocol Reference NSTableViewDataSource协议参考

Setting Values 设置值

- tableView:setObjectValue:forTableColumn:row: - tableView:setObjectValue:forTableColumn:row:

 Swift: optional func tableView(_ aTableView: NSTableView, setObjectValue anObject: AnyObject?, forTableColumn aTableColumn: NSTableColumn?, row rowIndex: Int) Objective-C: - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex 

Discussion : This method is intended for use with cell-based table views, it must not be used with view-based table views. 讨论 :此方法适用于基于单元格的表视图,不能与基于视图的表视图一起使用。 In view-based tables, use target/action to set each item in the view cell. 在基于视图的表中,使用target / action设置视图单元格中的每个项目。

If you're like me, you searched through the NSTableViewDelegate and NSTableViewDataSource protocols looking for some kind of edit method to use. 如果你像我一样,你搜索了NSTableViewDelegate和NSTableViewDataSource协议,寻找某种编辑方法。 However, the Discussion in the quote above is telling you that things are much simpler. 但是,上面引用的讨论告诉你事情要简单得多。

1) If you look in the document outline for your TableView in Xcode, you'll see something like this: 1)如果你在Xcode中查看TableView的文档大纲 ,你会看到如下内容:

在此输入图像描述

A cell in the TableView is represented by a Table Cell View . TableView中的单元格由Table Cell View The cell contains a few items, and by default one of the items is an NSTextField. 单元格包含一些项目,默认情况下,其中一个项目是NSTextField。 But where's the NSTextField in the document outline?! 但是文档大纲中的NSTextField在哪里?! Well, the controls in the document outline have an icon that looks like a slider next to their names. 好吧,文档大纲中的控件有一个图标,看起来就像名字旁边的滑块。 Take a look. 看一看。 Inside the cell, you'll see something which has a slider icon next to it. 在单元格内部,您会看到旁边有滑块图标的东西。 And, if you select that line in the document outline, then open the Identity Inspector, you'll see that it's an NSTextField: 并且,如果您在文档大纲中选择该行,然后打开Identity Inspector,您将看到它是NSTextField:

在此输入图像描述

You can consider that just a regular old NSTextField. 您可以认为这只是一个普通的旧NSTextField。

When you implement your NSTableViewDataSource protocol methods: 实现NSTableViewDataSource协议方法时:

import Cocoa

class MainWindowController: NSWindowController,
                            NSTableViewDataSource {
    ...
    ...
    var items: [String] = []  //The data source: an array of String's
    ...
    ...

    // MARK: NSTableViewDataSource protocol methods

    func numberOfRowsInTableView(tableView: NSTableView) -> Int {
        return items.count
    }

    func tableView(tableView: NSTableView,
                   objectValueForTableColumn tableColumn: NSTableColumn?,
                   row: Int) -> AnyObject? {
        return items[row]
    }

}

..the TableView takes the value returned by the second method and assigns it to a property named objectValue in the outer Table Cell View --in other words the TableView does not use the returned value to set the NSTextField (ie the inner Table View Cell ). .. TableView获取第二个方法返回的值,并将其分配给外部Table Cell View名为objectValue的属性 - 换句话说,TableView不使用返回值来设置NSTextField(即内部Table View Cell )。 That means your data source items will not be displayed in the TableView because the NSTextField is what displays an item. 这意味着您的数据源项目不会显示在TableView中,因为NSTextField是显示项目的内容。 In order to set the value of the NSTextField, you need to connect, or bind , the NSTextField's value to the objectValue property. 要设置NSTextField的值,需要将NSTextField的value连接或绑定到objectValue属性。 You do that in the Bindings Inspector: 您可以在Bindings Inspector中执行此操作:

Warning: Make sure you don't check the Bind to checkbox until after you select the object you want to bind to. 警告:确保在选择要绑定的对象之前,不要选中“ Bind to复选框。 If you check the checkbox first, an object will be inserted into your document outline, and if you don't notice it, you will get errors when you run your program. 如果先选中该复选框,则会在文档大纲中插入一个对象,如果您没有注意到,则在运行程序时会出现错误。 If you accidentally check the Bind to checkbox first, make sure you delete the automatically added object in your document outline. 如果您不小心首先检查“ Bind to复选框,请确保删除文档大纲中自动添加的对象。 Xcode creates a separate section for the added object, so it is easy to spot in your document outline. Xcode为添加的对象创建一个单独的部分,因此很容易在文档大纲中找到它。

在此输入图像描述

2) Backtracking for a moment, you are probably familiar with connecting a button to an action method, and thereafter if you click on the button the action method will execute. 2)回溯片刻,您可能熟悉将按钮连接到动作方法,此后如果单击按钮, 动作方法将执行。 On the other hand, with an NSTextField you typically declare an IBOutlet, which you then use to get or set the NSTextField's stringValue . 另一方面,使用NSTextField,您通常会声明一个IBOutlet,然后使用它来获取或设置NSTextField的stringValue

However, an NSTextField can also trigger the execution of an action method. 但是,NSTextField也可以触发执行操作方法。 Wha??! 什??! But you can't click on an NSTextfield like you can a button! 但你不能像按钮一样点击NSTextfield! Nevertheless, an NSTextField has a trigger, just like a button click, which will cause the execution of an action method, and the trigger is: done editing the NSTextField . 然而,NSTextField有一个触发器,就像一个按钮单击,这将导致执行一个动作方法,触发器是: 完成编辑NSTextField How does the NSTextField know when you are done editing it? NSTextField如何知道您何时完成编辑? There are two ways: 有两种方法:

  1. You hit Return. 你点击Return。

  2. You click on some other control. 你点击其他一些控件。

You can choose the trigger in the Attributes Inspector: 您可以在“属性”检查器中选择触发器:

在此输入图像描述

3) As @Paul Patterson showed in his answer, the next thing you need to do is set the NSTextField's Behavior to Editable in the Attributes Inspector. 3)正如@Paul Patterson在他的回答中所示,您需要做的下一件事是在属性检查器中将NSTextField的Behavior设置为可编辑

4) Then connect the NSTextField to the action method that you want to execute. 4)然后将NSTextField连接到要执行的操作方法。 If you haven't used the following trick to connect a control to an action method, you should try it some time: 如果您还没有使用以下技巧将控件连接到操作方法,那么您应该尝试一下:

Select your .xib file in the Project Navigator, so that the window and its controls are displayed. 在Project Navigator中选择.xib文件,以便显示窗口及其控件。 Then click on the Assistant Editor(the two_interlocking_rings icon at the top of the Xcode window on the far right)--that will display your Controller file(if some other file is shown, then use the jump bar to navigate to your Controller file). 然后单击Assistant Editor(最右侧Xcode窗口顶部的two_interlocking_rings图标) - 将显示您的Controller文件(如果显示了其他文件,则使用跳转栏导航到您的Controller文件) 。 Then Control+drag from the NSTextField (in the document outline) to the spot in your Controller file where you want to create your action method: 然后按住Control键并从NSTextField(在文档大纲中)拖动到您要在其中创建操作方法的Controller文件中的位置:

在此输入图像描述

When you release, you'll see this popup window: 当您发布时,您将看到此弹出窗口:

在此输入图像描述

If you enter the same information as shown, the following code will be entered in the file: 如果输入的信息与显示的信息相同,则会在文件中输入以下代码:

@IBAction func onEnterInTextField(sender: NSTextField) {

}

And...the connection between the NSTextField and the action method will already have been made. 并且......已经完成了NSTextField和action方法之间的连接。 (You also can use those steps to create and connect an IBOutlet.) (您也可以使用这些步骤创建和连接IBOutlet。)

5) Inside the action method, you can get the currently selected row, ie the one that has just been edited, from the TableView: 5)在action方法中,您可以从TableView获取当前选定的行,即刚刚编辑过的行:

@IBAction func onEnterInTextField(sender: NSTextField) {    
    let selectedRowNumber = tableView.selectedRow  //tableView is an IBOutlet connected to the NSTableView

}

Then I got stumped by how to get the text of the selected row in a TableView, and back to the docs I scrambled looking through the TableView and protocol methods. 然后我对如何在TableView中获取所选行的文本感到困惑,然后回到我通过TableView和协议方法查看的文档。 But, we all know how to get the stringValue of an NSTextField, right? 但是,我们都知道如何获取NSTextField的stringValue,对吧? The sender is the NSTextField you were editing: 发件人是您正在编辑的NSTextField:

@IBAction func onEnterInTextField(sender: NSTextField) {
    let selectedRowNumber = tableView.selectedRow  //My Controller has  an IBOutlet property named tableView which is connected to the TableView 

    if selectedRowNumber != -1 {  //-1 is returned when no row is selected in the TableView
        items[selectedRowNumber] = sender.stringValue  //items is the data source, which is an array of Strings to be displayed in the TableView
    }

}

If you don't insert the new value in the data source, then the next time the TableView needs to display the rows, the original value will get displayed again, overwriting the edited changes. 如果未在数据源中插入新值,则下次TableView需要显示行时,将再次显示原始值,覆盖已编辑的更改。 Remember, the TableView retrieves the values from the data source--not the NSTextField. 请记住,TableView从数据源检索值 - 而不是NSTextField。 Then the NSTextField displays whatever value the TableView assigned to the cell's objectValue property. 然后NSTextField显示TableView分配给单元格的objectValue属性的任何值。

One last thing : I got a warning that said I couldn't connect the NSTextField to an action inside a class if the class wasn't a delegate of the TableView....so I connected the TableView's delegate outlet to File's Owner: 最后一件事 :我收到一条警告说我无法将NSTextField连接到类中的动作,如果该类不是TableView的委托....所以我将TableView的委托出口连接到File的所有者:

在此输入图像描述

I had previously set the File's Owner to be my Controller(=MainWindowController), so after I made that connection, the MainWindowController--which contained the action method for the NSTextField--became the delegate of the TableView, and the warning went away. 我之前已经将File的Owner设置为我的Controller(= MainWindowController),所以在我建立连接后,MainWindowController(包含NSTextField的action方法)成为TableView的委托,警告消失了。

Random tips: 随机提示:

1) I found the easiest way to start editing an NSTextField is to select a row in the TableView, then hit Return. 1)我发现开始编辑NSTextField最简单的方法是在TableView中选择一行,然后点击Return。

2) NSTableView's come with two columns by default. 2)默认情况下,NSTableView有两列。 If you select one of the columns in the document outline, then hit Delete on your keyboard, you can make a one column table--however the TableView still shows the column divider, so it looks like there are still two columns. 如果选择文档大纲中的一列,然后单击键盘上的Delete,则可以创建一个列表 - 但TableView仍然显示列分隔符,因此看起来仍然有两列。 To get rid of the column divider, select the Bordered Scroll View - Table View in the document outline, then drag one of the corners to resize the TableView--the single column will instantly resize itself to take up all the available space. 要删除列分隔符,请在文档大纲中选择“边框Bordered Scroll View - Table View ”,然后拖动其中一个角来调整TableView的大小 - 单个列将立即调整大小以占用所有可用空间。

Credit for steps #1 and #2, and Random tip #2: Cocoa Programming For OS X (5th Edition, 2015). 感谢步骤#1和#2,以及随机提示#2:OS X的可可编程(2015年第5版)。

Full code listing: 完整代码清单:

//
//  MainWindowController.swift
//  ToDo
//

//import Foundation
import Cocoa

class MainWindowController: NSWindowController,
                            NSTableViewDataSource {

    //@IBOutlet var window: NSWindow? -- inherited from NSWindowController
    @IBOutlet weak var textField: NSTextField!
    @IBOutlet weak var tableView: NSTableView!

    var items: [String] = []  //The data source: an array of String's

    override var windowNibName: String {
        return "MainWindow"
    }

    @IBAction func onclickAddButton(sender: NSButton) {
        items.append(textField.stringValue)
        tableView.reloadData()  //Displays the new item in the TableView

    }

    @IBAction func onEnterInTextField(sender: NSTextField) {
        let selectedRowNumber = tableView.selectedRow

        if selectedRowNumber != -1 {
            items[selectedRowNumber] = sender.stringValue
        }
    }


    // MARK: NSTableViewDataSource protocol methods

    func numberOfRowsInTableView(tableView: NSTableView) -> Int {
        return items.count
    }

    func tableView(tableView: NSTableView,
                   objectValueForTableColumn tableColumn: NSTableColumn?,
                   row: Int) -> AnyObject? {
        return items[row]
    }

 }

The Connections Inspector showing all the connections for File's Owner (= MainWindowController): Connections Inspector显示File的Owner(= MainWindowController)的所有连接:

在此输入图像描述

By default, each cell (instance of NSTableCellView ) has an NSTextField sitting on it. 默认情况下,每个单元格( NSTableCellView实例)都有一个NSTextField When you're editing the cell, what you're in fact editing is this text field. 当您编辑单元格时,您实际编辑的是此文本字段。 Interface Builder makes this text-field non-editable: Interface Builder使此文本字段不可编辑:

在此输入图像描述

All you need to do is set the Behaviour pop-up to Editable . 您需要做的就是将“ 行为”弹出窗口设置为“ Editable Now you can edit the text-field with a return hit or a single -click. 现在你可以打回或单个键并单击编辑文本字段。

Just a correction to the accepted answer - you should get the row and column using the tableView.row(for: NSView) and tableView.column(for: NSView. Other methods may not be reliable. 只是对已接受的答案进行更正 - 您应该使用tableView.row(对于:NSView)和tableView.column(对于:NSView)获取行和列。其他方法可能不可靠。

@IBAction func textEdited(_ sender: Any) {
        if let textField = sender as? NSTextField {

            let row = self.tableView.row(for: sender as! NSView)
            let col = self.tableView.column(for: sender as! NSView)
            self.data[row][col] = textField.stringValue

            print("\(row), \(col), \(textField.stringValue)")

            print("\(data)")
        }
    }

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

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