Posted at

ヒレガス本の例題を Swift で書いてみる(3)

More than 3 years have passed since last update.

今回は第7章「キーバリューコーディング、そしてキーバリューの監視」に挑戦してみる。

NSObject には SetValue:forKeyvalueForKey: というメソッドが定義されており、その派生クラスのプロパティに対して文字列でアクセスできるようになっている。これをキーバリューコーディングといい、Cocoa ではこの機能を活用して View の各コントロールの属性と、Controller のプロパティをコーディングすることなしGUI画面で紐付けすることができるようになっていて大変お手軽である。

7章ではキーバリューコーディングの基本を学ぶ内容になっていて、作成するサンプルは非常に単純なものだ。

スクリーンショット 2014-11-08 11.53.06.png

ViewController (原書では独自に作成した AppController クラスだが、今回は XCode によって自動生成される ViewController クラスにコードを記述した)に設定された整数型のプロパティ fido を View に貼り付けたスライダーとラベルにキーバリューコーディングを利用して紐付ける(これをバインドという)。

まず、ViewController に 整数型のプロパティ fido を追加する。


ViewController.swift

class ViewController: NSViewController {

var fido = 50 // <- 追加したプロパティ

// 省略...
}


次に、追加したプロパティ fido を、Interface Builder の Bindings Inspector を利用してスライダーやラベルの Value 属性とバインドさせる。

スクリーンショット 2014-11-08 12.10.09.png

これだけでコントロールの Value 属性と ViewController の fido プロパティが紐付けられ、スライダーを操作するとその値が ViewController の fido プロパティに反映され、それがさらにラベルのテキストに反映されるようになる。

ここまでは何の問題もなし。

次に、fido プロアティの値をコードから変更した場合はどうなるのか? ここら辺から原書の Objective C 版と若干の違いが出てくる。

View に貼り付けたボタンから ViewController に Action を設定し、fido プロパティの値をインクリメントしてみる。


ViewController.swift

class ViewController: NSViewController {

var fido = 50 // <- 追加したプロパティ

@IBAction func incrementFido(sender: AnyObject) {
fido++ // <= プロパティの値をインクリメントする
}

// 省略...
}


このコードをコンパイル&実行してボタンをクリックしても、スライダーの位置やラベルのテキストに fido プロパティの値の変化は何ら反映されない。Objective C の @property 宣言を用いたプロパティは Java 風のアクセサを利用してアクセスすることになるが、そのアクセサ経由の変更は監視され View に自動的に通知される。しかし、Swift の場合、プロパティの変更を明示的に通知する必要があるみたいだ。

値の変更を通知するには、willChangeValueForKeydidChangeValueForKey を使えば良い。


Sample1

@IBAction func incrementFido(sender: AnyObject) {

willChangeValueForKey("fido")
fido++
didChangeValueForKey("fido")
}


あるいは、キーバリューコーディングを利用して


Sample2

@IBAction func incrementFido(sender: AnyObject) {

setValue(fido + 1, forKey: "fido")
}


としても良い。

また、Swift のプロパティには監視の仕組みとして willSetdidSet というオブザーバーをプロパティに指定することができるので、fido プロパティの定義を


Sample4

var fido : Int = 50 {

willSet {
willChangeValueForKey("fido")
}
didSet {
didChangeValueForKey("fido")
}
}


として、値の変更前後に willChangeValueForKeydidChangeValueForKey を呼び出すようにすれば、


Sample5

@IBAction func incrementFido(sender: AnyObject) {

fido++
}


と単純に値を変更するだけで通知を行うことができるようになる。