『TigerのCocoaにみるMVCの完成』のチュートリアルをやってみた(Leopardで)

Core Dataをそろそろ使ってみようかと思い、参考になりそうなサイトをぐぐったら、マイコミジャーナルの『【特集】TigerのCocoaにみるMVCの完成』という特集記事があった。『Dynamic Objective-C』や『Happy Macintosh Developing Time』の著者の木下誠さんという方が書かれたらしい。内容を読むと分かり易くかつ詳細に書かれていてとても参考になった。


特集記事の序盤にある、メールリーダ(っぽい外見を持ったもの*1 )をノンプログラミングで作成するチュートリアルは、Core Data + Cocoa Bindingsのパワフルさを垣間みれるとても良い内容だと思う。プログラマに限らず、プログラムやITに興味のある人でMacを持っているなら一度はやってみて欲しい。


記事ではチュートリアルといいながら細かい操作が書いてなくて、「理解はともかくとりあえず動かしてみたい」という人にはちょっと敷居が高いかもしれないので、具体的な操作手順を作ってみた。記事本文と合わせて読んでくれれば、とりあえず例示されているサンプルは作成できると思う。


記事の方は「Tigerの〜」と書いているけど、今回はLeopard(=Mac OS X 10.5)でやってみた。Tiger(10.4)とは若干違うらしい箇所もあったので、記事本文と両方見比べて自分の環境に合った手順を選んで欲しい。


背後の動作原理や設定内容の意味は書かない(自分もよく分かってない)ので、マイコミジャーナルの記事を読み進めるか、あと『Happy Macintosh Developing Time』という書籍の方にもCore Dataのチュートリアルが載ってるみたいなので、興味がある人は書籍の方も見てみると良いかも。


【目次】

  1. 新規プロジェクトの作成
  2. Core Dataモデルの設定
    1. エンティティを作成する
    2. エンティティに属性を追加する
    3. エンティティ間に関連を設定する
  3. インタフェースの作成
    1. Master/Detail Viewを作成する
  4. (おまけ)3ペインのユーザインタフェースに仕上げる

新規プロジェクトの作成

まず、開発環境であるXcodeを起動し、新規プロジェクトを作成する。その際にテンプレートとしては、Core Data Applicationを選択する。適当な名前をつけてプロジェクトを保存する。

http://journal.mycom.co.jp/special/2005/cocoamvc/001.html

適当な名前、とあるけど、ここではサンプル画像に従って新規プロジェクトのプロジェクト名には「MailCoreData」と入力しよう。タイトルが「MailCoreData」というウィンドウが表示される。

プロジェクトには、.xcdatamodelという拡張子を持つファイルが含まれる。これが、Tigerから追加された、Core Dataモデルの設計ファイルである。このファイルをダブルクリックして開く。すると、モデルエディタが立ち上がる。

「MailCoreData」ウィンドウ内の「MailCoreData_DataModel.xcdatamodel」という項目をダブルクリックする。すると「MailCoreData_DataModel.xcdatamodel」というタイトルの別ウィンドウが開く。これが「モデルエディタ」。

Core Dataモデルの設定

ここでは、簡単なメールソフトのために、MailとMailBoxというエンティティを作ってみた。Mailは、一通のメールに対応するエンティティだ。メールのアドレスを表すfromとto、メールタイトルを表すtitle、メールの内容を表すtextといった属性を設定した。すべて文字列型だ。MailBoxには、nameという名前を表す属性がある。

http://journal.mycom.co.jp/special/2005/cocoamvc/002.html
エンティティを作成する

MailとMailBoxというエンティティを作成する。


モデルエディタの左のペインの下の「+」ボタン(記事中では「エンティティ追加ボタン」)をクリックすることで、エンティティを追加できる。ボタンをクリックすると、左のペインに「Entity」と表示される。これが追加したエンティティとなる。



追加したエンティティはデフォルトで「Entity」という名前がついている。これを「Mail」という名前を変更する。「Entity」をクリックすると、右のペイン(記事中では「エンティティ編集パネル」)に入力フィールドが現れるので、エンティティ名を入力する。ここでは「Mail」と入力する。


同様にもう一つエンティティを追加し、「MailBox」に名前を変更する。

エンティティに属性を追加する

まずは「Mail」エンティティに「from」属性を追加する。属性を追加したいエンティティ(ここでは「Mail」)を左ペインでクリックして選択し、中央のペインの下の「+」ボタン(記事中では「プロパティ追加ボタン」)をクリックする。メニューが表示されるので「属性を追加」を選択する。すると中央のペインに「newAttribute」と表示される。これが追加した属性となる。



追加した属性はデフォルトで「newAttribute」という名前がついている。これを「from」という名前を変更する。「newAttribute」をクリックすると、右のペインに先ほどのエンティティ編集パネルとは異なる「属性」編集パネルが表示される。「属性」編集パネルの「名前」入力フィールドに属性名(「title」など)を入力する。


追加した属性を文字列型に設定するには、「名前」入力フィールドの下にある「データ型」というプルダウン(ポップアップリスト)で「文字列」を選択する


この手順を繰り返して、「Mail」に「to」「title」「text」を、「MailBox」に「name」を、いずれも「文字列」で追加する

エンティティ間に関連を設定する

さらに、この2つのエンティティの間に、それぞれを参照する関係を設定する。ここでは、MailはあるMailBoxに所属することにする。MailBoxは、複数のMailを保持することができるようにしよう。

http://journal.mycom.co.jp/special/2005/cocoamvc/002.html

まず「Mail」エンティティから「MailBox」エンティティへの関連「mailbox」を登録する。


関連を追加したいエンティティ(「Mail」)を左ペインでクリックして選択し、中央のペインの下の「+」ボタンをクリックする。メニューが表示されるので「関連を追加」を選択する。



中央のペインに「newRelationship」と表示される。これが追加した関連となる。追加した関連をクリックすると、「関連」編集パネルが表示される。「関連」編集パネルの「名前」入力フィールドに関連名(「mailbox」など)を入力する。また、「デスティネーション」というプルダウンで「MailBox」を選択する。すると「Mail」から「MailBox」への関連が設定され、下部のグラフにも関連が矢印で表示される。



同様に、「MailBox」エンティティに関連「mail」を追加し、デスティネーションを「Mail」とする。「逆」プルダウンで「mailbox」を選び、「対多関連」のチェックをONにする*2。これで二つのエンティティ間に相互に関連が設定される。オプションのチェックボックスを外し、「最小カウント」を削除する*3


モデルエディタ上でセーブ(メニューの「ファイル」>「保存」またはCtrl+S)して、これでモデリングは完了。

インタフェースの作成

Master/Detail Viewを作成する

まず、プロジェクトでMainMenu.nibをダブルクリックして開き、Interface Builderを立ち上げる。このとき、デフォルトで、GUIをデザインするためのウィンドウが1枚開くので、これをモデルエディタと重ならないように配置する。

http://journal.mycom.co.jp/special/2005/cocoamvc/002.html

プロジェクトウィンドウ(「MailCoreData」というタイトルのウィンドウ)でMainMenu.nib(English)をダブルクリックする。Interface Builderが立ち上がり、MainManu.nibというウィンドウが表示される。「GUIをデザインするためのウィンドウ」が開かない場合(というかLeopardでは多分開かない)、MainManu.nibウィンドウの中の「Window」というボタンをクリックする。グレー背景の空っぽのウィンドウが「GUIをデザインするためのウィンドウ」である。

次に、モデルエディタに戻り、Mailエンティティを選択、オプションキーを押しながらInterface Builderのウィンドウへとドラッグする。すると、「Create an interface for one or many Mail objects?(1つのMailオブジェクト用のインタフェースを作りますか、それとも複数Mailオブジェクト用?)」というダイアログが出るので、「Many Objects」を選ぶ。

Xcodeのモデルエディタの下のグラフに表示された「Mail」エンティティを、Optionキーを押しっぱなしにしながら、Interface Builderのウィンドウにドラッグ&ドロップする。


元記事には「〜というダイアログが出るので、「Many Objects」を選ぶ。」となっているが、Leopardではこの部分が少し違っていて、「Single Item View」「Master/Detail View」「Collection View」の中から選ぶようになっている。ここでは「Master/Detail View」を選ぶ。「Search Field」「Detail Fields」「Add/Remove」とチェックボックスが出てるのでとりあえず全部チェックする。「Next」をクリックして次の画面に進むとチェックボックスが並んでいるけど、そのまま「Finish」をクリックする。


上段に検索フォームとテーブルビューが、中段に「Remove」「Add」などのボタンが、下段に各項目の入力フィールドを含むウィンドウが表示される。これでセーブすれば、ひとまずインタフェースの作成は完了。

このインタフェースは、このままで動作する。試しに、Xcodeに戻り、ビルドして実行してみよう。さきほどのインタフェースが表示されて、すべての機能が動作する。Addボタンを押せばオブジェクトが追加され、内容を編集することができる。検索フィールドを使って、検索することもできる。アンドゥ、リドゥも使うことができる。保存と読み込みも自動的に行われる。いったん終了して再び起動すれば、前回の作業結果が残っていることが分かるだろう。


元記事にあるように、Xcodeでビルドして実行してみよう。Xcodeで「ビルドして進行」ボタンを押すと、先ほどInterface Builderで保存したウィンドウを持つアプリケーションが起動する。「Add」、「Remove」、項目の入力、検索、アンドゥ、ソート、カラムの並べ替えなどがおこなえる。

(おまけ)3ペインのユーザインタフェースに仕上げる

MailBoxのインタフェースも分解し、Mailのインタフェースと統合して、3ペインのユーザインタフェースに仕上げてみた。

http://journal.mycom.co.jp/special/2005/cocoamvc/002.html

とあるが具体的な手順が無いので調べてみたよ。

  1. Mail側の修正
    1. Boxのグルーピングを解く
      • 本文で「「Unpack Subviews」して」と書かれている操作。Leopardではメニューから「Layout」>「Unembed Objects」。この場合「Mail」と書かれたBoxを選択した状態でメニューを選ばないといけないが、若干面倒。ウィンドウ上の部品外の何も無いところをクリックしてからCtrl+Aを押すか、ドラッグしてBoxに掛かるようにすると選択できる。
    2. テーブルビューから「From」と「To」のカラムを削除
      • 「Text Cell」の部分をゆっくりクリックすると、選択部分が「Scroll View」「Table View」 「Table Column」「Text Field Cell」と変化するので、「Table Column」の時にDeleteキーかメニューの「Edit」>「Delete」で削除できる
    3. 「Fetch」ボタンを削除
    4. 「From:」と「To:」のラベルと入力フィールド(TextField)、「Mailox:」のラベルとプルダウンを削除
    5. 「Text」入力フィールド(一行入力のTextField)を削除
    6. 「Text:」用の入力フィールド(複数行入力可能なTextView)を追加する
      1. メニューから「ツール」>「ライブラリ」でライブラリパネルを表示する
      2. Cocoa」>「Views & Cells」>「Inputs & Values」を選ぶ
      3. ライブラリパネルから「Text View」(NSScrollView)をウィンドウにドラッグ&ドロップ
      4. 「Text View」を3回クリックしテキストを選択してから、メニューから「ツール」>「Attributes Inspector」でインスペクタを開き、インスペクタ上で「Allows」の「Rich Text」のチェックを外す
      5. メニューから「ツール」>「Bindings Inspector」でインスペクタを切り替え、インスペクタ上でValueをクリック、「Binding」をチェックし、プルダウンで「Mail Array Controller」を選択、「Controller Key」プルダウンで「selection」を、「Model Key Path」プルダウンで「text」を選ぶ。
      6. 完成図に合わせて部品を並び替え、セーブしてXcodeで実行
  2. MailBox側の作成
    1. MailBoxエンティティをXcodeからInterface Builder上のWindowにドラッグ&ドロップ
    2. 「Master/Detail View」を選ぶ。「Add/Remove」をチェックする。「Next」をクリックして次の画面に進むとチェックボックスが並んでいるので「Name」だけをチェックして「Finish」をクリックする。
    3. Boxのグルーピングを解いて(メニューから「Layout」>「Unembed Objects」、) 完成図に合わせて部品を並び替え、セーブしてXcodeで実行
  3. MailBox側とMail側の連携
    1. Interface Builderの編集中のファイル「MainMenu.nib」のウィンドウ上で「Mail Array Controller」を選択し、メニューから「ツール」>「Bindings Inspector」でインスペクタを表示する
    2. インスペクタ上で「Content Set」をクリックし、「Binding to:」をチェックし、プルダウンで二つ目の「Mail Box Array Controller」を選択、「Controller Key」プルダウンで「selection」を選択、「Model Key Path」プルダウンを選択してキーボードから「mail」と入力する。
    3. セーブしてXcodeで実行すると、左のMail Box側と右のMail側が連動するようになり、左で選択したメールボックスのメールが右側で表示されるようになる。
  4. Mail Box名のデフォルト値の設定
    • Xcodeのモデルエディタ上で「MailBox」,「name」を選択し、「属性」編集パネルの「デフォルト値」入力フィールドにデフォルト値(「New Mail Box」など)を入力する。
  5. MailのMailbox選択に空値を含めないようにする
    • Interface BuilderのMail側のテーブルビューで「Mailbox」カラムを選択し、メニューから「ツール」>「Bindings Inspector」でインスペクタを表示し、「Content Values」をクリックし、「Inserts Null Placeholder」のチェックを外す。
    1. セーブしてXcodeで実行すると、右のテーブルビュー内のMailboxカラムで、「No Value」が選択できなくなっている

これで大体「3ペインのユーザインタフェース」のイメージ通りの動きをするアプリケーションが出来る。

まとめ

ここまで、プログラム的なものは全く書かずに作成することが出来ている。ノンプログラミングが偉いという訳ではないが、各部品のなすべき仕事がきちんと整理され、それらを組み合わせてうまく動かす仕組みが出来ているからこそ、ここまでコーディングなしで出来るのだと思う。


ということでMacを持っている人は是非是非試してみて欲しい。Objective-Cユーザじゃなくても、参考にできそうなアイデアがきっと見つかるはず。

*1:っぽいであって、本当にメールサーバと通信する訳じゃないよ

*2:対多なので関連名は「mails」にしたいところだが、元記事の説明に合わせる

*3:これもサンプル画面の設定に合わせる為。