クリアメモリ

プログラミングやガジェットレビュー、便利なアプリケーションなど雑多に記録するブログ

【Swift】NSTableViewに別のビューからセルが追加できない

f:id:clrmemory:20170422183439p:plain 

先日、NSTableViewで作ったテーブルビューにセルを追加する方法を紹介しました。

 

その中で軽く触れたのですが、この記事で紹介した方法は同一ビューの中でしか機能しません。

別のビューからセルを追加するとエラーになるので、今回は別のビューからでもNSTableViewにセルを追加する方法を紹介します。

 

はじめに

 

前回同様、今回の記事でもSwiftとXcodeを使用しています。

インストールなどについては触れませんので、あらかじめ済ませておいてください。

 

前回の記事を確認していない方は、先にこちらから確認しておきましょう。

今回作成するアプリケーションと前回の記事で紹介した内容がかぶっている場合、一部省略する可能性があります。

 

https://clrmemory.com/swift/cocoa-add-tableview-cell/

 

こちらの記事で、NSTableViewにセルを追加する方法を紹介してるのですが、別のビューから追加しようとするとエラーが発生します。

 

 

例えば、ポップオーバーしたウィンドウからセルを追加しようとするとエラーが発生してしまうんですよね。

 

そこで今回は、以下のような形でNSTableViewにセルを追加する方法を紹介します。

 

 

今回の流れ

 

今回の流れは以下のようになっています。

 

1 ストーリーボードを作成

2 ビューをpopoverで接続

3 コードからNSTableViewの値を取得

4 別ビューからテキストの内容を取得

5 NotificationCenterで通知

6 通知を受けたらNSTableViewを更新

7 セルが追加される

 

このような順でストーリーボードやコードを作成していきます。

 

ではまず、ストーリーボードにTableViewや別のビューなどを配置していきましょう。

 

ストーリーボードを作成

 

今回作成するアプリケーションで必要になるオブジェクトは以下のものになります。

 

TableView (Cell Based)

Button (Popover)

View (Another View)

Label (Name and Age)

TextField (Text)

Button (Append)

 

では順番に作成していきましょう。

 

TableViewを配置

 

まずは、前回同様「TableView」をストーリーボード上に配置してください。

 

 

今回は「コラム数: 2」「ContentMode: Cell Based」で作成しました。

TableViewのTable Header Viewには「Name」と「Age」を指定しておきましょう。

 

 

ここで作成したコラムにそれぞれ「Identtifier」を設定する必要があります。

作成したコラムを選択した状態で、Identity inspectorを開いて「Identtifier」を以下のように編集してください。

 

 

 

これがないと後で紹介するコードで、入力が名前なのか年齢なのか判断できなくなります。

 

また、TableViewではデータソースを使用するので、dateSource接続しておいてください。

 

別のビューを呼び出すボタン

 

続いて、別のビュー(以下 > ビュー2)を表示するためのボタンを作成します。

 

今回は「+」と書かれたボタンをクリックすることで、ビュ-2がポップアップ表示されるようにしてみました。

( 設定方法はビュー2作成後に解説します)

 

 

ビュー2を作成

 

ビュー2では、名前と年齢を入力するウィンドウを表示させます。

 

以下の画像を参考にして、ビューを完成させてください。

 

 

NameとAgeに入力された文字列が、先ほど作成したTableViewのセルになります。

 

+ボタンとビュー2を接続

 

+ボタンをクリックした時にビュー2が表示されるようにするには「+ボタンからビュー2にセグエ」で接続してください。

 

 

ボタンからビューに接続すると「Action Segue」という項目が表示されるので、その中から「Popover」を選択しておきましょう。

 

 

 

このような表示になったかと思います。

ビュー2を表示する設定はこれだけです。

 

 

これでストーリーボードが完成しました。

アウトレット接続やアクション接続については、コードを書く時に一緒に説明します。

 

コードを書く

 

では実際にコードを作成しましょう。

今回は「ViewController」「Member」「AddMemberLiset」というクラスをそれぞれ作成してみました。

 

先に今回作成したコードを紹介した後で、順番に解説していきます。

 

ViewController -> TableViewを管理

 

import Cocoa

class ViewController: NSViewController, NSTableViewDataSource{

    @IBOutlet weak var tableView: NSTableView!

    var member =
        [
            Member(name: "Yamada", age: 21),
            Member(name: "Sato", age: 19),
            Member(name: "Suzuki", age: 25)
        ]

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(nCenter), name: NSNotification.Name("AddMember"), object: nil)
    }

    override var representedObject: Any? {
        didSet {}
    }

    // NotificationCenter >>>
    func nCenter(notification: Notification){
        let addMember = notification.userInfo?["member"]
        member.append(addMember as! Member)

        tableView.reloadData()
    }

    // tableview datasource >>>
    func numberOfRows(in tableView: NSTableView) -> Int {
        return member.count
    }
    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {

        let columns = tableColumn?.identifier

        if columns == "Name"{
            return member[row].name
        }else if columns == "Age"{
            return member[row].age
        }

        return ""
    }
}

 

 

Member -> 名前をString型、年齢をInt型で管理

 

class Member{
    var name: String
    var age: Int

    init(name: String, age: Int){
        self.name = name
        self.age = age
    }
}

 

 

AddMemberList -> ビュー2を管理

 

import Cocoa

class AddMemberList: NSViewController {

    @IBOutlet weak var nameText: NSTextField!
    @IBOutlet weak var ageText: NSTextField!

        override func viewDidLoad() {
            super.viewDidLoad()
            // Do view setup here.
        }
    @IBAction func appendButton(_ sender: NSButton) {

        let nameString = nameText.stringValue
        let ageInt = Int(ageText.stringValue)

        if nameString != "" && ageInt != nil{
            let member = Member(name: nameString, age: ageInt!)
            NotificationCenter.default.post(name: NSNotification.Name("AddMember"), object: nil, userInfo: ["member": member])

            nameText.stringValue = ""
            ageText.stringValue = ""
        }
    }
}

 

今回のコードはこのようになりました。

では順番に解説を確認してください。

 

解説

 

ViewController -> 作成したTableViewの表示や追加後の更新、NotificationCenterなどを記述してあります。

 

Memberクラス -> ViewControllerの配列memberに追加する値を初期化させるクラスです。

 

AddMemberList -> TableViewに名前と年齢に入力された文字列を取得し、セルを追加します。

 

ViewControllerの解説

 

まずはTableViewをアウトレット接続しています。

 

@IBOutlet weak var tableView: NSTableView!

 

 

続いて、配列memberの中に初期状態で表示する項目を追加しておきました。

 

これは空白でも構いません。

 

 

viewDidLoad( )ではNotificationCenterに通知を追加しています。

addObserver( )で通知の名前や、セレクタなどを指定する必要があり、NSNotification.Nameは後述するPostで設定した名前と同じにしてください。

 

NotificationCenter.default.addObserver(self, selector: #selector(nCenter), name: NSNotification.Name("AddMember"), object: nil)

 

 

nCenterでは通知を受けた時の処理を記述します。

 

// NotificationCenter >>>
func nCenter(notification: Notification){
    let addMember = notification.userInfo?["member"]
    member.append(addMember as! Member)

    tableView.reloadData()
}

 

先ほど同様、userInfo[ ]の文字列もNotificationCenter.default.postで設定したものと同じにしてください。

 

 

tableView( )では、先ほど設定したコラムのIdentifier (Identity) を判定しています。

 

let columns = tableColumn?.identifier

if columns == "Name"{
    return member[row].name
}else if columns == "Age"{
    return member[row].age
}

return ""

 

コラムのIdentityがNameなら…

コラムのIdentityがAgeなら…

 

Memberの解説

 

ViewControllerのMember( )では、名前と年齢をひとまとめにして配列に追加していました。

 

そのMemberをこのクラスで定義しました。

この記述がないと、ViewControllerはエラーになるので注意。

 

また、以下の画像にあるように「Target Membership」のプロジェクトにチェックが入っていることを確認してください。

 

 

※作成したプロジェクト名によって表示される内容は異なります。

 

AddMemberListの解説

 

最後にAddMemberListの解説になります。

 

このコードの中では、Appendボタンがクリックされたら「NotificationCenterに通知」を送るようになっています。

 

@IBAction func appendButton(_ sender: NSButton) {

    let nameString = nameText.stringValue
    let ageInt = Int(ageText.stringValue)

    if nameString != "" && ageInt != nil{
        let member = Member(name: nameString, age: ageInt!)
        NotificationCenter.default.post(name: NSNotification.Name("AddMember"), object: nil, userInfo: ["member": member])

        nameText.stringValue = ""
        ageText.stringValue = ""
    }
}

 

 

名前と年齢のテキストフィールドが空白ではない場合、ViewControllerに通知を送り、テーブルビューをリロードさせています。

このようにすることで、別のビューからでもテーブルビューを更新させることができました。

 

 

まとめ

 

今回紹介したように、テーブルビューに別のビューからセルを追加するには、NotificationCenterを使用することで実装できました。

 

これで、決まった内容のテーブルビューだけではなく、ユーザーが追加した項目を反映させることができますね。

とはいっても、保存処理を記述していないので、アプリを終了すると消えてしまいますが。

 

 

NSTableViewやNotificationCenterはとても便利なので、ぜひ活用してみてください。

ではまた。

新着記事