2016年7月3日日曜日

iOS テーブルビュー・サンプル(2):プレーンスタイル、インデックス付きリスト

今回は前回のポスト「iOS テーブルビュー・サンプル(1)」で作成した4つのストーリーボード参照の1番目(プレーンスタイルのテーブルビュー)と2番目(プレーンスタイルでインデックス付きリスト)のサンプルを作成します。
開発/実行環境:Xcode8_beta, Swift3.0, macOS Sierra 10.12 Beta

モデルの作成:

このサンプルアプリで使用するデータモデルを作成します。これはサンプル03, サンプル04 でも使用します。
  1. 連絡先クラス(Contact)の作成:
    _Model グループ下に、以下の内容の Contact.swift ファイルを作成します。

  2. 連絡先リポジトリクラス(ContactRepo)の作成:
    連絡先クラスを操作するためのクラスを作成します。
    _Model グループ下に、以下の内容の ContactRepo.swift ファイルを作成します。

    • 7-16行:簡単にするために、ContactRepo インスタンスの生成時に、ビューに表示する連絡先データを作成します。
    • 18-20行:イニシャライザで作成した連絡先データ全件を読み順にソートして返します。
    • 22-30行:指定した電子メールアドレスの連絡先データを返します。

画像の登録

このアプリで使用する画像をアセット・カタログに登録します。
登録するイメージは、連絡先の性別("maile1":男性と "femail":女性)を表します。


サンプル01:プレーンスタイルのテーブルビュー・サンプル

テーブルビューには、プレーンとグループの2つの主要なスタイルがあります。プレーンスタイルのテーブルビューは、1つ以上のセクションを持ち、セクショ ンは1つ以上の行を持ちます。また、各セクションは、それぞれ固有のヘッダタイトルやフッタタイトルを持つ事ができます。サンプル01では、このプレーンスタイルのテーブルビューを作成します。
  1. サンプル01用のグループの作成:
    1. プロジェクトナビゲータで、TableViewDemo グループ下に、_Table01 グループを作成します。
    2. 作成したグループを選択して、属性インスペクタでファイルシステムでの実際のフォルダの位置を指定します。
      (*1)位置選択ボタンをクリックすると、”choose a Location ”シートが表示される。
      (*2)"New Folder" ボタンをクリックして、"_Table01" フォルダを作成。
      (*3)”Choose” ボタンを押して、位置を指定。
      グループとファイルシステム上のフォルダを一致させておくとストーリーボードごとの管理が簡単になります。
  2. サンプル01用ストーリーボードを作成:
    グループ _Table01 に新規ファイル Table01.storybord を作成します。
    プロジェクトナビゲータで、_Table01 グループを選択して、command+N を押します。

    注意:上記画面は、Xcode_8_beta3 でのイメージです。
    表示された"新規ファイルのテンプレート選択" シートで iOS/Storyboard を選択して、"Next" ボタンをクリックします。 次のページで、保存ファイル名を ”Table01.storyboard" として作成します。
  3. 作成されたストーリーボードを選択して、オブジェクトライブラリからテーブルビューコントローラをキャンバスへドラッグして追加します。
  4. ナビゲーションコントローラに埋め込む:
    追加したテーブルビューコントローラを選択して、メニューで Editor > Embed In > Navigation Controller を選択して、ナビゲーションコントローラに埋め込みます。
    これにより、キャンバスにナビゲーションコントローラーが追加されます。
  5. ナビゲーションコントローラーをこのストーリーボードの初期ビューとして設定:
    追加されたナビゲーションコントローラを選択して、属性インスペクターで "Is Initial View Controller" をチェックします。
  6. 対応するビューコントローラクラスを作成:
    プロジェクトナビゲータで、_Table01 グループを選択して、command+N を押して、 上記のテーブルビューコントローラに対応する TableViewController01クラス(UITableViewController のサブクラス)を作成します。UITableViewControllerクラスはテーブルビューを管理し、選択の管理、行の編集、テーブルの設定などの標準的なテーブル関連動作に対するサポート機能を追加します。
  7. Table01 ストーリーボードのドキュメントアウトラインで、テーブルービューコントローラを選択します。
    アイデンティティインスペクタで、 選択したシーンの "class" フィールドに TableViewController01 を設定します。
  8. テーブルビューのコンテンツタイプとスタイルを設定:
    テーブルビューを選択して、属性インスペクタで、以下のように設定します。
  9. セルのスタイルと識別子を設定:
    テーブルビューセルを選択して、属性インスペクターでセルのスタイル、識別子を以下のように設定します。

  10. テーブルビューのデータソースを設定:
    TableViewController01 は、UITableViewController のサブクラスです。
    UITableViewController は、UITableViewDelegate, UITableViewDataSource プロトコルを実装します。
    データソース(UITableViewDataSource)は、アプリのデータモデルとテーブルビューを仲介する機能を提供します。
    TableViewController01.swift を開いて、次のように修正します。
    • 20行:連絡先リポジトリの findAll() 関数で、テーブルビューに表示する連絡先データ(よみ順にソート)を取得します。
    • 35-38:UITableViewDataSourceプロトコルの必須のメソッドの1つ。テーブルビューに対して、各セクションに表示する行数を伝えます。このテーブルビューのセクション数は1つなので、これはテーブルに表示する行数を示します。
    • 41-60:UITableViewDataSourceプロトコルの必須のメソッドの1つ。テーブルの各行に表示するセルを組み立てます。
      セルはUITableViewCellクラスを継承しています。
      • 42:ストーリーボードで定義したセル(識別子="TableView01Cell")をロードして再利用します。
        このセルは既成のセルスタイルとして、”Subtitle” を設定しています。
      • 45:対象行に表示する連絡先データを contacts から取得して、contact に代入します。
      • 46-52:"Subtitle" スタイルのセルには、コンテンツ表示用に3つのプロパティ(textLabel, detailTextLabel, imageView)が用意されています。
        これらのプロパティに取得した連絡先データの z氏名、z生年月日、z性別に対応する画像を設定します。
      • 53-58:セルのフォントやカラー、背景色、選択時のカラーなどをカスタマイズします。
  11. メインストーリーボードのサンプル01に対応するストーリーボード参照を選択して、属性インスペクタでストーリーボードとして、Table01 を設定します。

    これにより、マスターシーンのメニューの"サンプル01"を選択すると、Table01 で定義した、テーブルビューが表示されます。
    「iOS テーブルビュー・サンプル(1)」のストーリボード参照を使用するを参照してください。
  12. 実行結果


サンプル02:プレーンスタイル(インデックス付きリスト)のテーブルビュー・サンプル

サンプル02は、サンプル01と同様のプレーンスタイルのテーブルビューですが、インデックス付きリストの例を示します。インデックス付きリストは、特定のデータにすばやくアクセスできるように、セクション にインデックスを関連付けたものです。このサンプルでは"あ", "か", "さ"... ,"わ" のインデックスを表示します。"か"を選択すると、テーブルビューは "か"..<"さ" で始まる氏名の連絡先セクションまでスクロールします。
  1. _Table01 の場合と同様にしてTableViewDemo グループ下に、_Table02 グループを作成します。
  2. サンプル02用ストーリーボードを作成: グループ _Table02 に新規ファイル Table02.storybord を作成します。
  3. 作成されたストーリーボードを選択して、テーブルビューコントローラをキャンバスへ追加して、ナビゲーションコントローラに埋め込みます。
  4. 対応するビューコントローラクラス TableViewController02(UITableViewController のサブクラス)を作成します。
  5. テーブルービューコントローラを選択して、アイデンティティインスペクタで、 "class" フィールドに TableViewController02 を設定します。
  6. テーブルビューのコンテンツタイプとスタイルを設定:
    テーブルビューを選択して、属性インスペクタで、"Content" を "Dynamic Prototype"、"Style" を "Plain" に設定します。
  7. セルのスタイルと識別子を設定:
    テーブルビューセルを選択して、属性インスペクターで "Style" を "Subtitle"、"identifier" を "TableView02Cell" に設定します。

    ここまでは、サンプル01の場合とほとんど同様の操作です。
  8. テーブルビューのデータソースを設定:
    TableViewController02.swift を開いて、次のように修正します。
    • 15行:sections は、セクションのタイトル(title)、セクションに含まれる連絡先データの数(length)、そのセクションの先頭データの contents 配列内でのインデックス(index)を要素に持つタプルの配列です。
    • 32-58:連絡先データの配列(contacts)を zよみプロパティの値でセクションに分割します。
      • 38-57:contacts の全要素(zよみの値で昇順にソートされている)の zよみの値を調べてセクションに分割します。
        具体的には"あいだ", "いのうえ", ... などは、セクションタイトルが"あ"のセクションに、"かねこ", "きのした", ... は”か"のセクションに分割されます。
        実際には、セクションごとの連絡先データの配列に分割するのではなく sections 配列の要素を作成します。
    • 72-83:テーブルビューに表示するセルを返します。
    • 79行:sections の情報を元にして、表示行に対応する連絡先データの contacts 内のインデックス値を計算します。

  9. メインストーリーボードのサンプル02に対応するストーリーボード参照を選択して、属性インスペクタでストーリーボードとして、Table02 を設定します。
  10. サンプル02の実行結果



2016年7月2日土曜日

iOS テーブルビュー・サンプル(1):ストーリーボード参照を使ってストーリーボードを分割

「OS X : テーブルビューを使うサンプルアプリケーション」では OS X で動作するアプリを作成しました。このシリーズでは iOS でテーブルービューを使う場合のサンプルを作成します。ひとつのアプリで、様々なテーブルビューのサンプルを示すために、サンプルごとにストーリーボードを分割して、ストーリーボードを管理しやすくします。
開発/実行環境:Xcode8_beta, Swift3.0, macOS Sierra 10.12 Beta

プロジェクトの作成

テーブルビューをベースとしたアプリを作成する場合、プロジェクト作成で Xcodeに付属する Master-Detail Applicationテンプレートを選択すると、必要なビューやコードが自動的に準備されます。
  1. Xcodeのメニューで、
    File > New > Project... を選択。
  2. 新規プロジェクト作成シートで、
    iOS > Applicationセクション > Master-Detail Application テンプレートを選択して、Next をクリック。
  3. 次のページで次の画面のように設定(言語は swift を選択):

  4. 作成された新規プロジェクトの Main.storybord は図1のようになります。
    図1:プロジェクト作成直後のメイン・ストーリーボード:

    (*1):シミュレートするデバイスとして iPad Pro 9.7 を設定しています。
    (*2):Xcode8_beta では、ストーリーボードで表示するデバイスも選択できます。
    (*3):Xcode8_beta では、ズームも簡単になりました。


初期ビュー(メニュービュー)の作成

図1のマスターシーンのテーブルビューを修正して、初期表示のテーブルビューを、複数のテーブルビューサンプルを選択するためのメニュー(テーブル行がメニュー項目となる)として作成します。
  1. 自動作成された不要なファイルを削除:
    ブロジェクト・ナビゲータで、以下のファイルを削除します。
    • DetailViewController.swift
  2. 自動作成された AppDelegate.swift ファイルを以下のように修正:

  3. 自動作成された Main.storybord を以下のように修正:
    • セグエの削除:
      プロジェクト・ナビゲータで Main.storybord を選択して(図1が表示される)、不要なセグエを削除します。
    • マスターシーンをメニューテーブルに修正:
      1. ドキュメント・アウトラインで、マスターシーン(Master Scene)の Table View を選択します。
        属性インスペクタ/Table View セクション で以下を設定:
        • Content フィールド:"Static Cells"
        • Style フィールド:"plain"

        これにより、上記のように3つの静的なセルが自動的に作成されます。
      2. ドキュメント・アウトラインで、上の Table View の Table View Section を選択して、サイズ・インスペクターで以下のように行数を4行にします:
      3. ドキュメント・アウトラインで、テーブルビューのセクションの各セル(1...4)を選択して、タイトルを"サンプル01...04" に変更します。
    • 詳細シーンを修正:
      1. ドキュメント・アウトラインで、詳細シーン(Detail Scene)を選択します。
        アイデンティティインスペクタを開いて、Custom Class セクション/Class フィールド(DetailViewController がセットされている)をクリアします。
      2. ドキュメント・アウトラインで、この詳細シーンのビューのラベルを選択します。
        以下のようにして、このラベルのアウトレットを削除します:
    以上の結果として、メイン・ストーリーボードは次のように修正されます:


ストーリーボード参照を使用する

このサンプルアプリケーションでは、メニューテーブルのメニュー項目(テーブル行)が選択された場合に、各テーブルビューのサンプルへ遷移するようにします。
一つのストーリーボードで全てのテーブルビューサンプルのシーンを管理すると、ストーリーボードが非常に複雑になります。ストーリーボード参照を使用して、サンプルごとにストーリーボードを分割して、ストーリーボードを管理しやすくします。
  1. オブジェクトライブラリからストーリーボード参照をキャンバスへドラッグします:

    メニューテーブルの各行(サンプル01 ... サンプル04)に対応するように、上記の操作を繰り返して、4つのストーリーボード参照をキャンバスに追加します。
  2. マスターシーンのセル(サンプル01)を選択して、ストーリーボード参照へ CTRL + ドラッグします:

    表示されたポップアップで show detail を選択します:
  3. 残りの全てのセル(サンプル02..サンプル04)に対しても同様の操作を行います。
以上の結果、ストーリーボードは以下のようになります:



セルを装飾する

マスターシーン(MasterViewController)のテーブルビューの各セルを以下のように、MasterViewController.swift へアウトレットします:

MasterViewController.swift を次のように修正します:


実行結果:




次回以降は、それぞれのストーリボード参照に対応するテーブビューのサンプルを作成していきます

2016年6月30日木曜日

Core Data を使用する(2)

このシリーズの1回目、「Core Data を使用する(1)」では、3つのエンティティ(人物、社員、部署)を定義して、自動生成されたコード(Core Data スタック)について簡単に説明しました。今回は前回作成したプロジェクトを修正して、社員エンティティと部署エンティティの CRUD 機能(または、上位のビジネスロジック)を提供する社員サービスと、部署サービスを作成して、Core Data の使い方をもう少し掘り下げたいと思います。
開発/実行環境:Xcode8_beta, Swift3.0


部署サービスの作成

まず、プロジェクトナビゲータで ”_Service” グループを作成します。このグループ下に新規の BusyoService.swift ファイルを作成します。
_Service/BusyoService.swift:
  • 14-16:インスタンスの生成時に管理オブジェクトコンテキストを mc に設定。
  • 32-38:指定されたフィルタ条件のフェッチ要求を作成。
    • 33:部署エンティティに対するフェッチ要求を生成。
    • 34-36:検索条件が指定されたときのみ、生成したフェッチ要求に条件を設定する。
  • 21-24:登録された部署データを全件取得する。
    • 22:fetchRequest(pred:)関数により、フェッチ要求を生成する。
      引数を指定しないので、デフォルトの nil が渡される。
  • 41-46:指定された部署名の部署エンティティ・インスタンスを生成。
    • 42:部署エンティティの定義を取得する。
    • 43:取得した定義を元にインスタンスを生成する。
    • 44:プロパティに値を設定。
  • 49-51:指定した部署エンティティを削除する。


社員サービスの作成

同じようにして、SyainService.swift ファイルを作成します。
_Service/SyainService.swift:
CRUD 機能については、部署サービスと共通の部分が多いので、スーパークラスに共通部分をまとめることができます。このアプリではクラスの数も少ないので、そのままにしています。部署サービスと異なる部分(検索の部分)について以下で説明します。
  • 27-30:氏名検索:
    • 28-29: Predicate(format: "simei == %@", simei) の検索条件でフェッチ要求を作成して、氏名に対してフェッチを実行。
  • 32-35:氏名あいまい検索:
    検索条件を(format: "simei LIKE %@", simeiLike) のように指定。 simeiLike として "山田*" を指定すれば、氏名が山田で始まる社員を検索する。
  • 44-46:条件指定検索:
    引数として渡された検索条件で、社員を検索する。
  • 99-117: SyainMO の拡張:Swift の拡張を使って、SyainMO に計算プロパティを追加する。
    • 102-110: 勤続月数(KinzokuTuki)プロパティ:
      入社日(self.nyusyaBi)とシステム日付から勤続月数を求める。
      注意:本来はシステム日ではなく、退職予定日など指定した日付から求める。


AppDelegate.swift の修正

AppDelegate.swift ファイルの保存アクション(自動生成されたコード)を以下のように修正します
修正した AppDelegate.swift の saveAction(_:):
  • 4,5:部署サービス、社員サービスを生成。
  • 7,8:サービスの削除関数を使って、部署、社員の登録データを全件削除。
    注意:deleteAll()のような意味のわかる関数を作成すべきですね。
  • 10-11:部署データを2件作成。
  • 13-21:社員データを3件作成して、部署データとの関係を設定
  • 37-47:社員サービスの検索関数を使って、登録データを検索。
  • 50-60:検索結果をプリントするヘルパー関数。
    • 58: SyainService.swift の SyainMO の拡張で追加した、計算プロパティ(kinzokuNenGetu)を使って勤続年月をプリント。


アプリケーションのメニューの File/Save と保存アクションコードを結ぶ

上で修正した保存アクションの saveAction(_:)とメニューの File/Save を連結します。
ストーリーボードの Application シーンを開いて、AppDeleaget.swift/@IBAction 行の先頭の円からメニューの File/Save へドラッグします:



実行して結果を確認する

作成したアプリを実行して、メニューの File > Save を選択します。
コンソールに以下の内容が出力されます。

また、"/Users/bri_tcho/Library/Application%20Support/CoreDataDemo_Data/CoreDataDemo.storedata"ファイルは以下のようになります:
  • 8-33: saveAction(_:) の実行の結果として登録されたデータ(部署2件と社員3件)を確認できます。



2016年6月29日水曜日

Core Data を使用する(1)

Core Data は、以下のような機能を提供します。
  1. 変更の追跡機能。取り消し/再実行処理の支援。
  2. 変更の影響範囲の維持管理。
  3. オブジェクトの遅延読み込み、部分的な実体化によるオーバーヘッドの低減。
  4. 標準的な「キー値コーディング」検証メソッドを拡張したプロパティ値の自動検証。
  5. アプリのコントローラ階層との最適な統合による、UI同期の支援機能。
  6. データのグループ化、しぼり込み、組織化。
  7. 外部データリポジトリにデータを格納する処理の自動化。
  8. SQLを使わない複雑なクエリの生成。
  9. 楽観的ロック機構。
このフレームワークを使用することで、モデル階層の記述コード量を大幅に削減できます.
実際にサンプルアプリ(プロジェクト名: App)を作成して、Core Data の使い方を見ていきます。
開発/実行環境:Xcode8_beta, Swift3.0

エンティティとプロパティの生成

Xcode で、メニューバーから File > New > Project... を選択して新規プロジェクト(このデモでは Cocoa Application テンプレートを使用)を作成します。以下のように、プロジェクト作成時に
テンプレート選択ダイアログで「Use Core Data」をチェックします。

これにより、
Core Dataモデルのソースファイル App.xcdatamodeld(プロジェクト名.xcdatamodeld)が作成されます。

  • エンティティの生成
    自動生成された App.xcdatamodeld をナビゲータで選択して、Core Dataモデルエディタを表示します。

    1. 「Add Entity」をクリック。

      
ナビゲータ領域の「Entities」リストに新しい無題のエンティティが表示される。
    2. 新しい無題のエンティティを選択して、 Data Modelインスペクタで、エンティティ名、クラス名、必要なら親エンティティを入力してReturnを押します。
      ここで入力したクラス名(SyainMO)は、プログラムで参照するため識別しやすい名前にします。

      
 上の例のように親エンティティとして Jinbutu エンティティを指定する場合は、この Syain エンティティの定義前に Jinbutu エンティティを追加しておくことが必要です。
  • 属性と関係を生成
    1. 新規エンティティを選択して、生成先セクションの下部にあるプラス記号(+)をクリック。

      
新規の無題の属性または関係(総称して、プロパティと呼ぶ)が、
エディタ領域の「Attribute」セクションまたは「Relationship」セクションに追加される。 追加された属性や関係を選択して Delete キーを押せば削除できる。
    2. 追加プロパティを選択して、
Data Modelインスペクタの「Relationship」ペインまたは「Attribute」ペインで、プロパティの設定を行う。
      結果として、
属性または関係の情報がエディタ領域に表示される。プロパティ行で直接プロパティ名や型を修正することもできる。
  • エンティティ関連図(ER図)を表示
    上述のようにして、Jinbutu(人物:氏名,生年月日), Syain(社員:入社日), Busyo(部署:部署名) エンティティを作成して、属性と関係を追加。Syain は、Jinbutu を継承し、Syain : Busyo = 多 : 1 の関係です。
    モデルエディタの右下の Editor Style で ER図を選択。



Core Data スタック

プロジェクト作成時に「Use Core Data」をチェックすると、App.xcdatamodeld だけでなく、AppDelegate.swift ファイルに、Core Data スタックのコードが自動的に生成されます。スタックの主要な4つの要素と役割を以下に示します。
以下に示すコードは説明のため、自動生成されたコードのコメント文やメッセージを日本語に変更しています。
  • アプリのデータ保存用ディレクトリ:

    内容はコメントの通りです。(ファイル操作については次を参照:Swift 3.x:ファイル
    • 4行:lazy 指定により、このプロパティのコードは遅延実行される。
    • 11行:フォルダ名を"CoreDataDemo_Data"に変更。
      このデモアプリでは保存フォルダとして、
      "/Users/bri_tcho/Library/Application%20Support/CoreDataDemo_Data"
      を使用します。

  • 管理オブジェクトモデル:
    役割:モデルの構築。

    Core Data モデルの App.xcdatamodeld に対応します。
    開発者は、このオブジェクトを使って、定義したエンティティを操作することができます。
    • 7行:App.xcdatamodeld は実行時にコンパイルされて、App.momd として参照されます。
  • 永続ストアコーディネータ:
    役割:オブジェクトの永続化(永続化のファイル形式:SQLite, XML ファイル, メモリ, ..など)。

    管理オブジェクトモデル(エンティティ)の操作を実際の永続ストアに反映する。
    • 25-26行:アプリを初めて実行した場合、指定した保存フォルダは存在しないので、アプリドキュメントDIR + "/CoreDataDemo_Data"フォルダを作成。
    • 44行:コーディネータに25,26行で作成したフォルダ下の"CoreDataDemo.storedata"ファイルを永続ストアとして設定する。ファイル形式は XMLファイル。

  • 管理オブジェクトコンテキスト:
    役割:オブジェクトの管理。



データの永続化

自動生成された、その他のコードを見ながら、データの永続化がどのように行われるか見てみましょう。
まずはアプリを起動した後、なにもせずに終了(メニューで App > Quit App を選択)します。
AppDelegate.swift の以下の関数が実行されます:

  • 11-13行:Core Data の管理オブジェクトコンテキストに変更がないため、この条件判定が真となってアプリは終了します。
    ただし、managedObjectContext プロパティが参照された時点で、このプロパティを求めるコードが遅延実行されます。そのコード内では、persistentStoreCoordinator プロパティが参照され、同様にそのプロパティを求めるコードが遅延実行されます。その結果として、保存フォルダの "/Users/bri_tcho/Library/Application%20Support/CoreDataDemo_Data/"下に、空の"CoreDataDemo.storedata"ファイルが作成されます。
    プロパティについては次を参照:Swift 2.x: プロパティとメソッド

ここまで、3つのエンティティを Core Data モデルエディタで定義した以外にはほとんど何も行っていません。
上の関数を以下のように修正して、データの保存と、保存データの取得を行います。

  • 5-11:新しく追加したコード:
    • 7行:コンテキスト(managedObjectContext)から社員エンティ定義を取り出します。
    • 8行:その定義を元に社員エンティティ(SyainMOクラスのインスタンス)を生成して、コンテキスト(managedObjectContext)へ挿入します。
    • 9-11行:社員エンティティの各プロパティ(氏名、生年月日、入社日)に値を設定します。
  • 19行:コンテキストが変更されたので、この行が実行され、変更が保存されます。
  • 21-26行:新しく追加したコード:
    • 22行:フェッチ要求を生成します。特にフィルタ条件を設定していないので全件がフェッチ対象となります。
    • 23行:フェッチ要求を実行して、[SyainMO] 配列として結果を取得します。
    • 24-26:取得結果を表示します。

上記の修正を行った後、再度アプリを実行して、アプリメニューから App > Quit App を選択して、アプリを終了します。
"/Users/bri_tcho/Library/Application%20Support/CoreDataDemo_Data/CoreDataDemo.storedata"ファイルにデータが XMLとして保存されます。上記コードは社員データの新規追加なので、アプリを実行/終了するたびに、ファイルにはデータが追加されます。その結果、取得/表示データも増加します。

最後に

このデモアプリでは、以下の事を行いました。
  • モデルエディタでエンティティを追加して、属性と関係を設定する
  • 自動生成された Core Data スタックのコードの一部を変更してデータの永続化機能を調べる
次回のポスト「Core Data を使用する(2)」では、Core Data と UIとの連携や、より複雑なエンティティの定義などについて説明します。

2016年4月2日土曜日

OS X : コレクションビューを使うサンプルアプリケーション

コレクションビュー(NSCollectionView)を使ったサンプル・アプリケーションを作成します。

作業手順は次の通りです:

1.プロジェクト作成

スプリットビューコントローラを使ったテンプレートの作成と同様の手順で、次のような状態まで作業を進めます:


2.コレクションビューの追加

  • ストーリーボードで、
スプリットビューコントローラの左側ビューに、コレクションビューを追加します。
    自動レイアウト(ピンツールで親ビューにフィットするように設定)を設定します。
    
コレクションビューを追加すると同時にコレクションビューアイテムも追加されます。
    このコレクションビューアイテムは削除しておきます。
  • コレクションビューを選択して、属性インスペクタでレイアウトを flow に変更します。
    その他の属性も変更します(青枠で囲った部分)。
  • このコレクションビューを追加したビューコントローラを選択して、アイデンティティインスペクタで、Class に ViewController を設定します。
  • ここまでの作業で、ストーリーボードの状態は次のようになります:


3.モデルの作成

ナビゲーション・ペインの Model グループで 
MovieTicket (NSObject のサブクラス)を作成します。
このクラスは、前売り券のイメージと映画のタイトルを保持します。
/Model/MovieTicket.swift:



4.コレクションビューをコードに接続

  • ストーリーボードでコレクションビューを選択して、ViewController クラスへ CTRL+ドラッグします:
    参照:アシスタントエディタ

  • 表示されたポップアップで次のように入力します:

  • 次のように outlet が作成されます:



5.コレクションビューアイテムの作成

  • Model グループ下に 
CollectionViewItem.swift (NSCollectionViewItem のサブクラス) を作成します。
    このとき、xib も作成します:

    CollectionVIewItem は representedObject として MovieTicket インスタンスを保持します。

  • 同様に Model グループ下に ItemView.swift(NSView のサブクラス)を作成します。

  • CollectionViewItem.xib を選択して、
    • オブジェクトライブラリから、項目のビューにラベルとイメージを追加して、適当なサイズに調整します:
    • ラベルを選択して属性インスペクターで以下のように設定します:

    • オブジェクトライブラリから Object を追加します:

    • ドキュメントアウトラインで上記のオブジェクトを選択して、アイデンティティインスペクタで、Class を CollectionViewItem に設定します:
    • 同じようにドキュメントアウトラインで View を選択して、アイデンティティインスペクタで、Class を ItemView に設定します:
    • ラベルを選択して、バインディングインスペクタで次のように設定します:
    • イメージを選択して、バインディングインスペクタで次のように設定します:
    • Object から view へ CTRL +ドラッグします:
    • 表示されたポップアップで view を選択します:
  • 上記の一連の作業の結果、コレクションビューアイテムオブジェクトのバインディングの状態は次のようになります:


6.サンプル・アプリケーションで使用する画像を追加

参照:アセットカタログへ画像を登録


7.コレクションビューのデータソースを実装

/Controller/ViewController.swift を次のように修正します:

  • 4行:NSCollectionViewDataSource を継承する
  • 29行:ViewController をコレクションビュー・データソースとして設定。
  • 34-48行:データソースとして必要なメソッドを実装する。
    • 34-37行:コレクションビューに表示する項目数を返す。
    • 42-43行:NSCollectionView の makeItemWithIdentifier() メソッドを使って indexPath で指定されたコレクションビューアイテム(CollectionViewItem のインスタンス)を生成して、representedObject として MovieTicket インスタンスを設定する。


8.コレクション項目の選択時の処理を追加

項目の選択時に枠や背景色を変更するために ItemView を次のように修正します:


CollectionViewItem も次のように修正します: