web-dev-qa-db-ja.com

ビューベースのNSTableView編集

何が悪いのかわかりません。これに関する他の質問(またはドキュメント)を見つけることができなかったので、他の人には問題なく正常に動作しているようです。

私は単にそのコンテンツの編集をサポートするためにビューベースのNSTableViewを取得しようとしています。つまりアプリは、いくつかのコンテンツを含むNSTextFieldを含む、1つの列と複数の行を持つNSTableViewを表示します。セルを(ダブル)クリックして、セルのコンテンツを編集できるようにしたいと思います。したがって、基本的に、セルベースの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

ストーリーボードファイルは次のようになります。 Storyboard

テーブルビューのデータソースとデリゲートがビューコントローラーに設定されます。このアプリを実行すると、テーブルビューに10個のテスト行が表示されますが、行の1つを編集することはできません。

何故ですか?ここで何を逃したのですか?

NSTableViewのすべての属性(およびその内容)をダブルチェックして、AppleのTableViewPlaygroundサンプルと同じであることを確認しました。そして、何時間もドキュメンテーションとインターネットを検索して何の成功もない有用なヒントを探した後、私はちょっとイライラしています。ビューベースのNSTableViewで見つけることができるのは、編集不可能なサンプルまたは編集可能なコンテンツに関する非常にあいまいな情報です。そしてもちろん、編集可能なセルベースのNSTableViewsに関する情報、ドキュメント、サンプルがたくさんあります...

サンプルプロジェクトのZipは、こちらからダウンロードできます。 TableTest.Zip

16
Zaggo

デフォルトでは、各セル(NSTableCellViewのインスタンス)にはNSTextFieldが置かれています。セルを編集しているとき、実際に編集しているのはこのテキストフィールドです。 Interface Builderはこのテキストフィールドを編集不可能にします:

enter image description here

BehaviourポップアップをEditableに設定するだけです。これで、リターンヒットまたはsingle-clickでテキストフィールドを編集できます。

28
Paul Patterson

viewベースのNSTableViewを編集するためのすべての部分がQ&Aに含まれていますが、すべてをまとめるのにまだ問題がありました。次のデモはSwiftでXcode 6.3.2を使用していますが、objective-Cの穴居人/女性にとっては簡単に理解できるはずです。完全なコードリストは最後にあります。

ここから始めましょう:

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のドキュメントアウトラインを見ると、次のように表示されます。

enter image description here

TableViewのセルはTable Cell Viewで表されます。セルにはいくつかのアイテムが含まれており、デフォルトではアイテムの1つはNSTextFieldです。しかし、ドキュメントのアウトラインのNSTextFieldはどこにありますか?!まあ、ドキュメントのアウトラインのcontrolsには、名前の横にあるスライダーのようなアイコンがあります。見てください。セル内には、スライダーアイコンが横にあるものが表示されます。そして、ドキュメントのアウトラインでその行を選択してIdentity Inspectorを開くと、それがNSTextFieldであることがわかります。

enter image description here

これは、通常の古い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は2番目のメソッドによって返された値を受け取り、それを外側のTable Cell ViewobjectValueという名前のプロパティに割り当てます。つまり、TableViewは戻り値を使用してNSTextFieldを設定しません。 (つまり、内側のTable View Cell)。つまり、NSTextFieldがアイテムを表示するため、データソースアイテムはTableViewに表示されません。 NSTextFieldの値を設定するには、NSTextFieldのvalueをobjectValueプロパティに接続またはbindする必要があります。これはバインディングインスペクターで行います。

警告:バインドするオブジェクトを選択するまでは、Bind toチェックボックスをオンにしないでください。最初にチェックボックスをオンにすると、オブジェクトがドキュメントのアウトラインに挿入されます。気付かないと、プログラムの実行時にエラーが発生します。最初に誤ってBind toチェックボックスをオンにした場合は、ドキュメントのアウトラインから自動的に追加されたオブジェクトを削除してください。 Xcodeは、追加されたオブジェクト用に個別のセクションを作成するため、ドキュメントのアウトラインを簡単に見つけることができます。

enter image description here

2)しばらくバックトラックします。ボタンをactionメソッドに接続し、その後ボタンをクリックするとactionメソッドが実行されます。一方、NSTextFieldでは通常、IBOutletを宣言します。これを使用して、NSTextFieldのstringValueを取得または設定します。

ただし、NSTextFieldはactionメソッドの実行をトリガーすることもできます。なに?しかし、ボタンのようにNSTextfieldをクリックすることはできません!それでも、NSTextFieldにはボタンクリックのようにトリガーがあり、actionメソッドが実行され、トリガーはdone editing NSTextField。 NSTextFieldは、編集が完了したことをどのようにして知るのですか? 2つの方法があります。

  1. Returnキーを押します。

  2. 他のコントロールをクリックします。

属性インスペクターでトリガーを選択できます。

enter image description here

@Paul Pattersonが彼の回答で示したように、次に行う必要があるのは、NSTextFieldのBehaviorEditableに設定することです。属性インスペクタ。

4)次に、NSTextFieldを実行するアクションメソッドに接続します。次のトリックを使用してコントロールをアクションメソッドに接続していない場合は、しばらくしてから試す必要があります。

プロジェクトナビゲーターで.xibファイルを選択すると、ウィンドウとそのコントロールが表示されます。次に、アシスタントエディター(右端のXcodeウィンドウの上部にあるtwo_interlocking_ringsアイコン)をクリックします。コントローラーファイルが表示されます(他のファイルが表示されている場合は、ジャンプバーを使用してコントローラーファイルに移動します)。 。次に、NSTextField(ドキュメントのアウトライン内)から、アクションメソッドを作成するコントローラファイルの場所にControl +ドラッグします。

enter image description here

リリースすると、次のポップアップウィンドウが表示されます。

enter image description here

上記と同じ情報を入力すると、次のコードがファイルに入力されます。

@IBAction func onEnterInTextField(sender: NSTextField) {

}

そして... NSTextFieldとアクションメソッドの間の接続はすでに行われています。 (これらの手順を使用して、IBOutletを作成および接続することもできます。)

5)アクションメソッド内で、現在選択されている行、つまり編集されたばかりの行をTableViewから取得できます。

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

}

次に、TableViewで選択した行のテキストを取得する方法に困惑し、TableViewとプロトコルメソッドを調べてスクランブル処理したドキュメントに戻りました。しかし、NSTextFieldのstringValueを取得する方法は誰でも知っていますよね? senderは、編集していた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プロパティに割り当てた値を表示します。

最後に1つ:クラスがTableViewのデリゲートでない場合、NSTextFieldをクラス内のアクションに接続できないという警告が表示されたので、TableViewのアウトレットをファイルの所有者に委任します。

enter image description here

以前にファイルの所有者をコントローラー(= MainWindowController)に設定していたので、その接続を行った後、MainWindowController(NSTextFieldのアクションメソッドを含む)がTableViewのデリゲートになり、警告が消えました。

ランダムなヒント:

1)NSTextFieldの編集をstartする最も簡単な方法は、TableViewで行を選択してReturnキーを押すことです。

2)NSTableViewには、デフォルトで2つの列があります。ドキュメントアウトラインの列の1つを選択し、キーボードの[削除]を押すと、1列のテーブルを作成できます。ただし、TableViewにはまだ列分割線が表示されているため、2つの列があるように見えます。列の区切り線を取り除くには、ドキュメントのアウトラインでBordered Scroll View - Table Viewを選択し、コーナーの1つをドラッグしてTableViewのサイズを変更します。単一の列はすぐにサイズが変更され、使用可能なすべてのスペースを占めます。

ステップ#1と#2のクレジット、およびランダムヒント#2:OS X用のCocoaプログラミング(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]
    }

 }

ファイルの所有者(= MainWindowController)のすべての接続を示す接続インスペクター:

enter image description here

54
7stud

受け入れられた回答に対する修正だけです-tableView.row(for:NSView)とtableView.column(for: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)")
        }
    }
2