[英]View based NSTableView editing
我不确定我做错了什么。 由于我找不到任何关于此的任何其他问题(甚至文档),似乎通常对其他人没有问题。
我只是想尝试使用基于NSTableView的视图来支持编辑它的内容。 即应用程序显示带有一列和多行的NSTableView,其中包含带有一些内容的NSTextField。 我希望能够(双)单击一个单元格并编辑单元格的内容。 所以基本上是基于NSTableView的单元格的正常行为,其中实现了tableView:setObjectValue:forTableColumn:row:
方法。
我分析了Apple的TableViewPlayground示例代码中的Complex TableView示例(支持编辑单元格内容),但我找不到启用编辑的设置/代码/开关。
这是一个简单的示例项目(Xcode 6.1.1,SDK 10.10,基于故事板):
标题:
#import <Cocoa/Cocoa.h>
@interface ViewController : NSViewController
@property (weak) IBOutlet NSTableView *tableView;
@end
执行:
#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
故事板文件如下所示:
表视图的数据源和委托设置为视图控制器。 运行此应用程序时,表视图显示10个测试行,但无法编辑其中一行。
这是为什么? 我在这里想念的是什么?
我仔细检查了NSTableView的所有属性(及其内容)与Apple的TableViewPlayground示例中的相同。 经过几个小时的搜索文档和互联网有用的提示没有任何成功,我有点沮丧。 您在基于视图的NSTableViews上找到的所有内容都是不可编辑的样本,或者是关于可编辑内容的非常模糊的信息。 当然,在可编辑的基于单元格的NSTableViews上有大量的信息,文档和示例......
我的示例项目的zip可以在这里下载: TableTest.zip
即使编辑基于NSTableView的视图的所有部分都存在于问题和答案中,我仍然无法将它们整合在一起。 以下演示使用Xcode 6.3.2在Swift中进行,但对于Objective-C cavemen / womens应该很容易理解。 最后是一个完整的代码清单。
我们从这里开始:
NSTableViewDataSource协议参考
设置值
- 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
讨论 :此方法适用于基于单元格的表视图,不能与基于视图的表视图一起使用。 在基于视图的表中,使用target / action设置视图单元格中的每个项目。
如果你像我一样,你搜索了NSTableViewDelegate和NSTableViewDataSource协议,寻找某种编辑方法。 但是,上面引用的讨论告诉你事情要简单得多。
1)如果你在Xcode中查看TableView的文档大纲 ,你会看到如下内容:
TableView中的单元格由Table Cell View
。 单元格包含一些项目,默认情况下,其中一个项目是NSTextField。 但是文档大纲中的NSTextField在哪里?! 好吧,文档大纲中的控件有一个图标,看起来就像名字旁边的滑块。 看一看。 在单元格内部,您会看到旁边有滑块图标的东西。 并且,如果您在文档大纲中选择该行,然后打开Identity Inspector,您将看到它是NSTextField:
您可以认为这只是一个普通的旧NSTextField。
实现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]
}
}
.. TableView获取第二个方法返回的值,并将其分配给外部Table Cell View
名为objectValue
的属性 - 换句话说,TableView不使用返回值来设置NSTextField(即内部Table View Cell
)。 这意味着您的数据源项目不会显示在TableView中,因为NSTextField是显示项目的内容。 要设置NSTextField的值,需要将NSTextField的value
连接或绑定到objectValue属性。 您可以在Bindings Inspector中执行此操作:
警告:确保在选择要绑定的对象之前,不要选中“
Bind to
复选框。 如果先选中该复选框,则会在文档大纲中插入一个对象,如果您没有注意到,则在运行程序时会出现错误。 如果您不小心首先检查“Bind to
复选框,请确保删除文档大纲中自动添加的对象。 Xcode为添加的对象创建一个单独的部分,因此很容易在文档大纲中找到它。
2)回溯片刻,您可能熟悉将按钮连接到动作方法,此后如果单击按钮, 动作方法将执行。 另一方面,使用NSTextField,您通常会声明一个IBOutlet,然后使用它来获取或设置NSTextField的stringValue
。
但是,NSTextField也可以触发执行操作方法。 什??! 但你不能像按钮一样点击NSTextfield! 然而,NSTextField有一个触发器,就像一个按钮单击,这将导致执行一个动作方法,触发器是: 完成编辑NSTextField 。 NSTextField如何知道您何时完成编辑? 有两种方法:
你点击Return。
你点击其他一些控件。
您可以在“属性”检查器中选择触发器:
3)正如@Paul Patterson在他的回答中所示,您需要做的下一件事是在属性检查器中将NSTextField的Behavior
设置为可编辑 。
4)然后将NSTextField连接到要执行的操作方法。 如果您还没有使用以下技巧将控件连接到操作方法,那么您应该尝试一下:
在Project Navigator中选择.xib文件,以便显示窗口及其控件。 然后单击Assistant Editor(最右侧Xcode窗口顶部的two_interlocking_rings图标) - 将显示您的Controller文件(如果显示了其他文件,则使用跳转栏导航到您的Controller文件) 。 然后按住Control键并从NSTextField(在文档大纲中)拖动到您要在其中创建操作方法的Controller文件中的位置:
当您发布时,您将看到此弹出窗口:
如果输入的信息与显示的信息相同,则会在文件中输入以下代码:
@IBAction func onEnterInTextField(sender: NSTextField) {
}
并且......已经完成了NSTextField和action方法之间的连接。 (您也可以使用这些步骤创建和连接IBOutlet。)
5)在action方法中,您可以从TableView获取当前选定的行,即刚刚编辑过的行:
@IBAction func onEnterInTextField(sender: NSTextField) {
let selectedRowNumber = tableView.selectedRow //tableView is an IBOutlet connected to the NSTableView
}
然后我对如何在TableView中获取所选行的文本感到困惑,然后回到我通过TableView和协议方法查看的文档。 但是,我们都知道如何获取NSTextField的stringValue,对吧? 发件人是您正在编辑的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
}
}
如果未在数据源中插入新值,则下次TableView需要显示行时,将再次显示原始值,覆盖已编辑的更改。 请记住,TableView从数据源检索值 - 而不是NSTextField。 然后NSTextField显示TableView分配给单元格的objectValue属性的任何值。
最后一件事 :我收到一条警告说我无法将NSTextField连接到类中的动作,如果该类不是TableView的委托....所以我将TableView的委托出口连接到File的所有者:
我之前已经将File的Owner设置为我的Controller(= MainWindowController),所以在我建立连接后,MainWindowController(包含NSTextField的action方法)成为TableView的委托,警告消失了。
随机提示:
1)我发现开始编辑NSTextField最简单的方法是在TableView中选择一行,然后点击Return。
2)默认情况下,NSTableView有两列。 如果选择文档大纲中的一列,然后单击键盘上的Delete,则可以创建一个列表 - 但TableView仍然显示列分隔符,因此看起来仍然有两列。 要删除列分隔符,请在文档大纲中选择“边框Bordered Scroll View - Table View
”,然后拖动其中一个角来调整TableView的大小 - 单个列将立即调整大小以占用所有可用空间。
感谢步骤#1和#2,以及随机提示#2:OS X的可可编程(2015年第5版)。
完整代码清单:
//
// 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]
}
}
Connections Inspector显示File的Owner(= MainWindowController)的所有连接:
默认情况下,每个单元格( NSTableCellView
实例)都有一个NSTextField
。 当您编辑单元格时,您实际编辑的是此文本字段。 Interface Builder使此文本字段不可编辑:
您需要做的就是将“ 行为”弹出窗口设置为“ Editable
。 现在你可以打回或单个键并单击编辑文本字段。
只是对已接受的答案进行更正 - 您应该使用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.