[英]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.