第13章
第13章
ユーザデフォルト
前章では環境設定パネルを作りました。この章ではその環境設定パネルにメインウィンドウの背景色を変更できる機能を搭載します。
しかしこの環境設定パネルでの背景色の変更はMyClipを再起動するとアプリケーションのデフォルトの設定に戻ってしまいます。そこでユーザの設定を保持するための仕組み用意しなければなりません。このユーザの設定を保持する仕組みのことをユーザデフォルトと呼びます。
説明の都合上、本格的なコーディングはこの節の最後で行います。しかし環境設定パネルからのターゲットアクション接続を行うためにはIBActionメソッドをControllerクラスに用意しておかなければなりません。そこでIBActionメソッドの実装は後回しにして宣言と定義だけを先に行っておきます。前章までのプロジェクトを残しておかれる場合はいつものとおりバックアップを作成してから作業をはじめてください。
●IBActionメソッドの追加
Controller.h
強調箇所が追加するメソッド宣言です。なお今回はこのControllerのコードリストでは変更に関係のない部分の掲載は省略しています。ご了承ください。
メソッド宣言はC言語のプロトタイプと同じ働きをします。したがってどのメソッド宣言から記述しなければならないというような順番はありません。もし順番をつけるとすればそれはプログラマからの見やすさのためになります。しかし読者が迷われてもいけませんので一応@endの前の最後の行に記述するということにいたします。
- (IBAction)selectedRadioButton:(id)sender;
@end
Controller.m
メソッドの定義も記述する順番に規則はありません。しかし読者の利便を考えdeallocメソッドの手前ということいたします。
- (IBAction)selectedRadioButton:(id)sender
{
}
- (void)dealloc
{
[model
release];
[super
dealloc];
}
@end
13. 1. 1 NSMatrix
ではInterface Builderでの作業をはじめます。XcodeのMyClipプロジェクトウィンドウでMainMenu.xibをダブルクリックするとInterface BuilderでMainMenu.xibファイルが開きます。
●作業のためにPreferencesパネルを移動する
Preferencesパネルが表示されていないようであればMainMenu.xibウィンドウのPanel(MyClip Preferences)をダブルクリックして表示させてください。
まずこれから作業するPreferencesパネルの位置が作業のしづらい位置にあることが分かります。自分自身が作業のやりやすい場所へドラッグしてもらっても構いません。
図を見ていただいても分かるとおりパネルを移動してもPanel SizeインスペクタのInitial Positionの位置は変わりません。アプリケーションを起動した時のウィンドウやパネルの表示位置はInterface Builderで作業するときの位置とは連動しません。Interface Builderで表示されている位置と同じ位置に起動後も表示させたい場合には前章で行ったようにUse Currentボタンを押します。しかしボタンを押したあとからまたウィンドウやパネルを移動してもそれが起動後の位置に連動し続けるということはありません。
図作業のためにパネルを移動する Xcode 3.1 図の大きさは適宜調整お願いいたします。
図作業のためにパネルを移動する Xcode 3.2
DRAFT
●Radio Groupの配置
【Xcode 3.1の場合】
LibraryインスペクタパネルのObjectsタブを選択します(通常はObjectsタブが選択されていると思います)。Library→Cocoa→Views
& Cellsを選ぶとLibraryインスペクタパネルの下の一覧にRadio Groupというのがあります。このRadio GroupをMyClip Preferencesパネルにドラッグ&ドロップします。
図Radio Group Xcode 3.1 図の大きさは適宜調整のほどお願いいたします。
【Xcode 3.2の場合】
LibraryインスペクタパネルのObjectsタブを選択します(通常はObjectsタブが選択されていると思います)。Library→Cocoa→Views
& Cellsを選ぶとLibraryインスペクタパネルの下の一覧にRadio Groupというのがあります。このRadio GroupをMyClip Preferencesパネルにドラッグ&ドロップします。
図Radio Group Xcode 3.2
DRAFT
●マトリックス
Preferencesパネルに配置したRadio Groupを選択した状態でインスペクタパネルのタイトルを見るとMatrix○○となっています。このMatrixは正式にはNSMatrixのことになります。NSMatrixは複数のボタンなどを実質的にひとつのボタンとして扱ってコストを下げる働きをします。実際にラジオボタンの場合は複数のボタンを選択することは出来ません。表面上にはいくつものボタンがあったとしてオブジェクト全体としてはどれが選ばれたのかだけわかれば良いことになります。
図Matrix Attributes Xcode
3.1 図の大きさは適宜調整のほどお願いいたします。
図Matrix Attributes Xcode
3.2
DRAFT
Matrix AttributesインスペクタパネルのCells項目ではRowsで行数をColumnsで列数を指定します。デフォルトの設定では2行の1列という設定になっていることになります。この設定を、Rowsを5にColumnsを2に設定します。設定するにはそれぞれのフィールドに直接数字を入力しても構いませんしフィールドの右横の上下の矢印をクリックして数値を変更することもできます。
図Matrix Attributes Cells
Xcode 3.1
図Matrix Attributes Cells
Xcode 3.2
DRAFT
しかしPreferencesパネルを見るとマトリックスが少し変な状態になっています。これはInterface Builderが物の位置を、左下を基点にして決めるためにこのようになってしまっています。ようするに上の2行がパネルの外に出てしまっているのです。
図Preferencesパネル1
Matrixをドラッグして下に移動します。いつものようにガイドラインに合わせて配し直してください。
図Preferencesパネル2
●ラジオボタンのタイトル設定
今回は5行2列のラジオボタンを配置したことになります。それぞれのボタンのタイトル部分をダブルクリックするとタイトルが編集可能状態になります。それぞれのタイトルを次ぎの名前にしてください。タイトルの設定はインスペクタパネルでもできますが、このMatrixのタイトル設定もダブルクリックして直接編集するほうが確実です。
時間があって表にしたほうが良ければ表にしてください。私はこのままでもかまいません。
左の列 右の列
black blue
brown cyan
gray green
magenta orange
purple white
それぞれのボタンを編集中(選択中)にはインスペクタパネルはButton Cell○○になっていると思います。
図Preferencesパネル3
ラジオボタンのタイトルが長くなりましたので表示が途中で切れているものもあると思います。Matrixが選択されている状態で、「Layout」メニュー→「Size To Fit」を選びます。
図:Layout - Size To Fit 図の大きさは適宜調整のほどお願いいたします。
Matrixの大きさがタイトルの文字列に合わせてちょうどよい大きさになります。
図Preferencesパネル4 図の大きさは適宜調整のほどお願いいたします。
●Matrixのターゲットアクション接続
Matrixを選択状態にして右クリック+ドラッグでPreferencesパネルのMatrixからMainMenu.xibウィンドウのControllerオブジェクトへ接続線を引きます。
図Matrixターゲットアクション接続1 図の大きさは適宜調整のほどお願いいたします。
Controllerオブジェクトが強調表示されてからマウスのボタンを離すと接続先の一覧が現れます。Received
Actions項目からselectedRadioButton:をクリックしてターゲットアクションの接続を確定します。
図Matrixターゲットアクション接続2 図の大きさは適宜調整のほどお願いいたします。
13. 1. 2 NSBox
●NSBox
ターゲットアクションの接続は完了しましたがPreferencesパネルの10個のラジオボタンはこのままではまとまりもなく何のためのラジオボタンなのかも分かりません。そこでこのラジオボタンがひとつのまとまりとして見えるようにして何のためのボタンなのかキャプションも表示されるようにしたいと思います。
Matrixが選択されている状態で「Layout」メニュー→「Embed Objects In」アイテム→「Box」を選びます。PreferencesパネルのMatrixがBoxと呼ばれる少しへこみのある角丸四角に囲まれます。
図Embed Objects In - Box 図の大きさは適宜調整のほどお願いいたします。
この状態でインスペクタパネルのタイトルはBox○○に変わっていると思います。一番左のAttributesタブを選んでTitleを次のように設定してください。
Background
color of Text View
図Box Attributes 図の大きさは適宜調整のほどお願いいたします。
おそらく変わりはないと思いますが念のためにSize
To FitでBoxのサイズを適正なものにしておきます。PreferencesパネルでBoxが選択されている状態で
「Layout」メニュー→「Size To Fit」を再び実行してください。
図Preferencesパネル5 図の大きさは適宜調整のほどお願いいたします。
そしてガイドラインに沿ってNSBoxの位置を調整します。
図Preferencesパネル6 図の大きさは適宜調整のほどお願いいたします。
Preferencesパネルの大きさもガイドラインに沿って調整します。
図Preferencesパネル7 図の大きさは適宜調整のほどお願いいたします。
13. 1. 3 NSButtonCell
それぞれのRadio ButtonはNSMatrixのなかでButton Cellとして認識されています。そしてそれぞれのButton Cellを識別するためにそれぞれのButton CellはTagという整数値を持っています。
Tagは正確にはNSButtonCellのスーパークラスNSActionCellで定義されているインスタン変数です。変数名も正しくはtagと小文字で始まります。そしてこのtag変数に格納される値の型はNSIntegerになっています。
現状ではそれぞれのButton CellのTagは
「Black」ラジオボタンのTagが1
そのほかのラジオボタンのTagはすべて0
という自動で振り分けられたデフォルトの設定になっています。この値を、それぞれのラジオボタン(正しくは各Button Cell)ごとに違う値に設定していきます。
まずBlackラジオボタンをクリックしてください。現在みなさんが何かを選択されている状態なのかどうか分かりませんが、もし最初に何も選択されていなければまずMatrixが選択されます。何が選択されたかは常にインスペクタパネルで確認してください。
図Preferencesパネル8
この状態でもう一度BlackラジオボタンをクリックするとインスペクタパネルのタイトルがButton Cell ○○になると思います。次にAttributesタブを選んでください。TitleがBlackでTagが1であればBlackラジオボタン(Button
Cell)が選ばれているのは間違いありません。しかしPreferencesパネルではマトリックス全体が選択されているようで気持ち良くはありません。
図Button Cell Tag設定1
そこでBlackラジオボタンをもう一度クリックしてください。インスペクタパネルの内容は変わらないと思いますが、選択されているのがBlackラジオボタンであることが分かる表示に変わると思います。このBlack
Button CellではTagの数値はデフォルトの1のままにしておきます。
図Button Cell Tag設定2 図の大きさは適宜調整のほどお願いいたします。
この要領で各Button Cellを選択していきそれぞれのTagの数値を次のように設定します。
なお前述の説明では2度クリックして見た目でも個別のButton Cellが選択されていることが分かる表示にしましたが、Matrix全体が選択されているように見える状態でもインスペクタパネルのタイトルがButton Cell○○であれば個別のButton Cellが選択されています。どのCellが選択されているかはTitleで確認できます。そしてこの状態で作業を行っていただいてもまったく問題はありません。
□各Button Cell Tagの設定値
時間があって表にしたほうが良ければ表にしてください。私はこのままでも構いません。
black 1
blue 2
brown 3
cyan 4
gray 5
green 6
magenta 7
orange 8
purple 9
white 0
以上でInterface Builderでの作業は終了いたしました。作業を保存してXcode に戻ります。
13. 1. 4 NSColor
Xcodeに戻りましたら一度「ビルドして進行」で実行してみましょう。MyClipウィンドウは通常センターとされているデスクトップ画面中央のやや上寄りに表示されます。MyClipメニューのPreferences...を選んでPreferencesパネルも表示させてみましょう。設定どおり左上に表示されます。しかしselectedRadioButton:アクションメソッドの実装していませんのでラジオボタンの選択を変更しても何の変化も今は起こりません。
図:MyClip実行画面1 図の大きさは適宜調整のほどお願いいたします。
Preferencesパネルのラジオボタンを有効にするためにController.mのselectedRadioButton:メソッドを次のようにコーディングします。今回はすべての実装コードが新たに追加されることになりますが、いつものとおり追加・変更されたコードは強調表示するようにいたしました。
●コードリスト Controller.m selectedRadioButton:
-
(IBAction)selectedRadioButton:(id)sender
{
enum tagValue {
WHITE, BLACK, BLUE, BROWN, CYAN, GRAY,
GREEN, MAGENTA, ORANGE, PURPLE
};
id selectedCell = [sender selectedCell];
if ([selectedCell tag] == BLACK)
[textView setBackgroundColor:[NSColor
blackColor]];
if ([selectedCell tag] ==BLUE )
[textView setBackgroundColor:[NSColor
blueColor]];
if ([selectedCell tag] == BROWN)
[textView setBackgroundColor:[NSColor
brownColor]];
if ([selectedCell tag] == CYAN)
[textView setBackgroundColor:[NSColor
cyanColor]];
if ([selectedCell tag] == GRAY)
[textView setBackgroundColor:[NSColor
grayColor]];
if ([selectedCell tag] == GREEN)
[textView setBackgroundColor:[NSColor
greenColor]];
if ([selectedCell tag] == MAGENTA)
[textView setBackgroundColor:[NSColor
magentaColor]];
if ([selectedCell tag] == ORANGE)
[textView setBackgroundColor:[NSColor
orangeColor]];
if ([selectedCell tag] == PURPLE)
[textView setBackgroundColor:[NSColor
purpleColor]];
if ([selectedCell tag] == WHITE)
[textView setBackgroundColor:[NSColor
whiteColor]];
}
●実行
コーディングが終わりましたら「ビルドして進行」をクリックして実行してみましょう。今度はPreferencesパネルでのラジオボタンの選択変更がMyClipのテキストビューの背景色に反映されます。
図:MyClip実行画面2 図の大きさは適宜調整のほどお願いいたします。
●マトリックスとセル
前項まではNSMatrixとButton
Cellの扱い方だけを説明しました。しかしコードについての説明になると、マトリックスとセルについてもう少し詳しい説明が必要になってきます。
マトリックス
ラジオボタンも本来はPush Buttonなどと同じくNSButtonの一種です。今回のようにラジオボタンが多数配置された場合はNSButtonの2つ上のスーパークラスであるNSViewが多数配置されることになります。
このNSViewは非常にコストの高いオブジェクトです。そこでラジオボタンのようにXOR(排他的論理和)として常にひとつのボタンだけが選択されていることを表すだけで良い場合は、これらのラジオボタンのViewをマトリックスという代理的なひとつのビューにまとめてコストを下げる方法がとられます。
セル
このマトリックスの中で個別のラジオボタンはCellというオブジェクトで区別されます。
まとめ
以上の考え方がマトリックのはじまりですが、現在ではCPUの速度も早くなりコストの節約という面よりも、表面上は多くの選択肢があっても実際にはひとつしか選択できないものなどをまとめるのに便利なものとして使われているようです。
●コード説明
enum tagValue {
WHITE, BLACK,
BLUE, BROWN, CYAN, GRAY, GREEN, MAGENTA, ORANGE,
PURPLE
};
今回利便性のためにC言語のenumを使いました。しかしenumはまだint型にしか対応していないかもしれません(もしかするとすでに64bit long型に対応しているのかもしれませんが)。一方tagメッセージで取得できる値はNSIntegerです。NSIntegerは32bitコンピュータの場合はint型として解釈されますので問題ありませんが64bitコンピュータの場合はlong型として解釈されます。64bitコンピュータをお使いでもしこの項のコードで問題が出るようでしたらenumの使用をあきらめてそれぞれのif文の条件式に色を表す定数名ではなく数値を用いるように変更してみてください。
id selectedCell = [sender selectedCell];
マトリックス(この場合はsenderがマトリックスを表しています)に対してselectedCellというメソッドを送るとその時に選択されているセルが戻り値として返ってきます。その返ってくる値のオブジェクトはマトリックスを構成しているセルによって違います。今回のMyClipのようにButton Cellと限っているわけではありません。したがってその戻り値が代入される変数selectedCellの型はidにしておいたほうが良いでしょう。
if ([selectedCell tag] == BLACK)
取得したセルに対してtagメッセージを送信するとそのセルのtagインスタン変数に代入されている整数値が取得できます。Tagの値が1の場合は「black」ラジオボタンが選ばれたことが分かります。
[textView setBackgroundColor:[NSColor blackColor]];
Text Viewに対してsetBackgroundColor:メッセージを送っています。このメッセージ(メソッド)はNSTextViewのスーパークラスのNSTextで定義されているインスタンスメソッドです。何を行うメソッドかはメソッド名から分かっていただけると思います。興味がわくとすればこのメソッドの引数でしょう。NSColorはその名前のとおり色を表すクラスです。そのクラスに送っているblackColorというメッセージはクラスメソッドになります。詳しくはNSColor Class Referenceを見てください。
図:NSColor Class Reference 図の大きさは適宜調整のほどお願いいたします
Creating an NSColor with Preset Componentsといグループに前もって用意されている15通りの色を作成するクラスメソッドが用意されています。
この2つのコードは次のようにif文の条件部分と実行部分になります。
if ([selectedCell tag] == BLACK)
[textView
setBackgroundColor:[NSColor blackColor]];
これでテキストビューの背景色を黒にしています。そして黒・青・茶・...と背景色を選ぶif文が10個並んでいることになりますが、if文は最初のif文から最後のif文まですべて実行されます。ようするに無駄が多いわけです。これを防ぐにはif文の実行式にreturn文を書いて条件式に該当した場合は背景色の設定を行ってメソッド自体を抜けるか(終了するか)、else ifでつないで条件式に該当したところで実行式を実行してif文を終わらせるなどの方法があります。しかしどちらの方法も条件式に該当するまではif文の作業は続きます。そこでおすすめするのがif文の代わりにswitch文を使う方法です。
if文を次のswitch文に置き換えてください。念のためにif文も残しておきたい場合はif文全体を
/* と */ で囲んでコメント化してください。
switch ([selectedCell tag]) {
case BLACK:
[textView setBackgroundColor:[NSColor
blackColor]];
break;
case BLUE:
[textView setBackgroundColor:[NSColor
blueColor]];
break;
case BROWN:
[textView setBackgroundColor:[NSColor
brownColor]];
break;
case CYAN:
[textView setBackgroundColor:[NSColor
cyanColor]];
break;
case GRAY:
[textView setBackgroundColor:[NSColor
grayColor]];
break;
case GREEN:
[textView setBackgroundColor:[NSColor
greenColor]];
break;
case MAGENTA:
[textView setBackgroundColor:[NSColor
magentaColor]];
break;
case ORANGE:
[textView setBackgroundColor:[NSColor
orangeColor]];
break;
case PURPLE:
[textView setBackgroundColor:[NSColor
purpleColor]];
break;
case WHITE:
[textView setBackgroundColor:[NSColor
whiteColor]];
break;
}
これでコーディングは完成しました。しかしPreferencesパネルで背景色を設定してもMyClipを再起動するとデフォルト設定の白い背景色に戻ってしまいます。
これはMyClipの終了時にユーザの設定した環境設定値が保存されていないからです。次節ではこのユーザが設定した環境設定値が次にMyClipを起動した時にも反映されるようにしたいと思います。反映されるようにユーザデフォルトという仕組みを使ってみたいと思います。
13. 2 ユーザデフォルト
ユーザデフォルト(User Defaults)はデフォルトデータベースとも呼ばれています。前節では環境設定パネルで行ったユーザ設定はアプリケーションを再起動するとアプリケーションのデフォルト設定に戻ってしまいました。このような場合にはユーザによって設定された値はカレントユーザディレクトリの「ライブラリ」フォルダ→「Preferences」フォルダのアプリケーション識別名.plistに保存しなければアプリケーションを終了するとなくなってしまいます。アプリケーション識別名.plistにユーザの設定が保存されていればアプリケーションの再起動時にこのplistファイルからユーザの設定値を読み込んでアプリケーションの設定をユーザの設定にあわせます。
ユーザデフォルトの設定については比較的詳しく解説されている場合が多く、ついつい難しいもののように思いがちですが、その使用方法はおそらく読者の想像よりずっとやさしいものだと思います。
ユーザデフォルトの設定とその使用方法は比較的やさしいものだと思います。
●NSUserDefaults
ユーザの個別の設定は前述のとおり/ユーザディレクトリ/ライブラリ/Preferences/アプリケーション識別名.plistにASCII形式のプロパティリストで保存されています。XML形式のプロパティリストについてはのちの章で登場してきます。説明いたします。そしてこのユーザデフォルトのplistファイルへ設定を書き込んだり、あるいはplistファイルから設定を読み込んだりするには、まずNSUserDefaultsクラスのインスタンスを次のメッセージ式で取得します。
[NSUserDefaults
standardUserDefaults]
NSUserDefaultsのインスタンスはアプリケーションひとつに対して一個しか作られません。このメッセージ式を何度実行しても返されるNSUserDefaultsのインスタンスは同じものになります。
ユーザの個別の設定を保存する場合にはこのNSUserDefaultsのインスタンスに対してユーザの値を設定のためのメッセージを送ります。そしてアプリケーション起動時にはこのNSUserDefaultsのインスタンスに対してユーザの設定を取得するためのメッセージを送ります。
●コーディング
前節までのプロジェクトを残しておきたい場合には適宜バックアップをとってから作業をはじめてください。
Controller.h
Controller.hに次のメソッド宣言を追加します。記述する場所はインスタン変数の宣言の終わりを表す } からこのヘッダファイルの終わりを表す@endまでの間であればどこでも構いません。しかし一応最後の行の@endの前ということにしておきます。
-
(void)setTextViewColor:(NSInteger)tag;
このメソッドは前節で定義したselectedRadioButton: アクションメソッドから呼び出される内部メソッドになります。
Controller.m
前節ではselectedRadioButton:アクションメソッドで行っていた背景色の設定を前述のsetTextViewColor: 内部メソッドで行うように変更しています。なぜなら今回はアプリケーション起動時にも背景色の設定を行うことになります。したがってアクションメソッド以外からでも呼び出せるメソッドにしておく必要があります。
コードを記述する場所としてはselectedRadioButton:メソッドの次にsetTextViewColor:メソッドを記述するようにいたしました。今回もメソッドの記述順序がプログラムに影響を与えることはありません。ただ私たちが読みやすいであろうと思う順序にしているだけです。
●selectedRadioButton:メソッド
-
(IBAction)selectedRadioButton:(id)sender
{
/* 選択されているボタンセルの取得
*/
id selectedCell = [sender selectedCell];
/* ボタンセルのTagの数値の取得
*/
NSInteger tag = [selectedCell tag];
/ * setTextViewColor:内部メソッドへのメッセージ送信
*/
[self setTextViewColor:tag];
}
●説明
id selectedCell = [sender selectedCell];
前節で一応説明したコードですがid型変数selectedCellを宣言して同時にsenderつまりマトリックスの中で現在選択されているセルオブジェクトで初期化しています。
NSInteger tag = [selectedCell tag];
NSInteger型の変数tagを宣言して同時に1行目で取得したセルオブジェクトのTagの整数値で初期化しています。
[self
setTextViewColor:tag];
内部メソッドのsetTextViewColor:メソッドへ2行目で取得したどのセルオブジェクトかを表す整数値を引数としてメッセージを送っています。
●setTextViewColor:メソッド
-
(void)setTextViewColor:(NSInteger)tag
{
enum tagValue {
WHITE, BLACK, BLUE, BROWN, CYAN, GRAY,
GREEN, MAGENTA, ORANGE, PURPLE
};
/* switch文による分岐
*/
switch (tag) {
case BLACK:
[textView setBackgroundColor:[NSColor
blackColor]];
break;
case BLUE:
[textView setBackgroundColor:[NSColor
blueColor]];
break;
case BROWN:
[textView setBackgroundColor:[NSColor
brownColor]];
break;
case CYAN:
[textView setBackgroundColor:[NSColor
cyanColor]];
break;
case GRAY:
[textView setBackgroundColor:[NSColor
grayColor]];
break;
case GREEN:
[textView setBackgroundColor:[NSColor
greenColor]];
break;
case MAGENTA:
[textView setBackgroundColor:[NSColor
magentaColor]];
break;
case ORANGE:
[textView setBackgroundColor:[NSColor
orangeColor]];
break;
case PURPLE:
[textView setBackgroundColor:[NSColor
purpleColor]];
break;
case WHITE:
[textView setBackgroundColor:[NSColor
whiteColor]];
break;
}
[[NSUserDefaults standardUserDefaults] setInteger:tag
forKey:@"textViewColor"];
}
●説明
前節のselectedRadioButton:メソッドからid selectedCell = [sender selectedCell];を除いた残りの式をすべてこちらのメソッドに移して少し変更を加えています。
switch (tag) {
前節ではswitch文の式の中で[selectedCell tag]メッセージ式を取得していましたが今回はこのsetTextViewColor:メソッドの引数として受け取った整数値をそのまま式の値としています。
[[NSUserDefaults standardUserDefaults] setInteger:tag forKey:
@"textViewColor"];
前述のとおりNSUserDefaultsクラスに対してstandardUserDefaultsメッセージを送るとNSUserDefaultsのインスタンスが得られます。そのインスタンスに対してユーザの設定値を設定するインスタンスメソッドをメッセージとして送っています。この値を設定するインスタンスメソッドには次のようなものが用意されています。
今回はこの中からsetInteger:forKey:を使っています。このメソッドの正確なシグネチャは次のとおりになります。
- (void)setInteger:(NSInteger)value
forKey:(NSString
*)defaultName
このメソッドでは何のための値かを識別するためにキーと呼ばれる文字列を名前代わりに付けます。このキーはUserDefaultsの中で同じものがあってはいけません。
次に、コーディング場所の順序が前後しますがawakeFromNibメソッドにも強調箇所のコードを追加します。
●awakeFromNibメソッド
- (void)awakeFromNib
{
NSInteger
tag;
[textView setString:@""];
if ([model string]) {
[textView insertText:[model string]];
[window setDocumentEdited:NO];
[textView
scrollRangeToVisible:NSMakeRange(0, 0)];
}
[window center];
/*
UserDefaults からの設定を反映させる */
tag =
[[NSUserDefaults standardUserDefaults]
integerForKey:@"textViewColor"];
[self
setTextViewColor:tag];
}
ここではNSUserDefaultsのインスタンスに対して設定値を取得するメッセージを送っています。その際にどの設定値を取得したいかをさきほど説明したキーによって指定します。この設定値を取得するインスタンスメソッドには次のようなものが用意されています。
これらのメソッドはユーザデフォルトのなかに該当するキーの設定値がない場合にはnilを返します。今回はこの中からintegerForKey:メソッドを使っています。正確なシグネチャは次のようになります。
- (NSInteger)integerForKey:(NSString
*)defaultName
「ユーザデフォルトのなかに該当するキーの設定値がない場合にはnilを返します」 整数値の型のnilは0として見なされます。このためユーザデフォルトに何の設定のない場合にはセルオブジェクトのTagの数値が0に該当する色がテキストビューの背景色に設定されます。 enumの設定のところでWHITEを先頭にしたのは0=WHITEに設定してユーザデフォルトに何の設定値もない場合はテキストビューの背景色を白にするためだったのです。
●実行
コーディングが終わりましたらファイルを保存して「ビルドして進行」をクリックしてください。最初は白の背景色でMyClipウィンドウが起動すると思います。Preferencesパネルを表示して背景色を設定してMyClipを一度終了させてください。そしてもう一度起動します。
おめでとうございます。今度は設定した背景色でMyClipが起動したことだろうと思います。
しかし残念なことにPreferencesパネルを開くと表示されている背景色とは関係なくblackラジオボタンが選ばれていることだろうと思います。このラジオボタンは現在表示している背景色と連動するようにするべきでしょう。
図MyClip実行画面3
初期化されていない変数の値 数値などを格納する変数の場合、変数を定義した段階で初期化される前に処理系によって一定の数値が代入される場合があります。その値はほとんど場合において0であることが多いです。しかし必ずしも0であるとは限りません。今回のサンプルコードでもNSInteger型のtag変数を定義した段階で代入される値は0ではありません。ユーザデフォルトのintegerForKey:メソッドによってnilが返された段階ではじめて0が代入されます。 このことはデバッガを使った時やテストをするためのコードを書くと良く分かると思いますが残念ながら基礎編ではデバッギングについての説明は省略させてもらうことになっています。ただ、tag変数は定義された時点ですでに0になっていたのではなく、あくまでもintegerForKey:によって0になったことをご理解いただければ助かります。
●コード変更
Controller.h
インスタン変数の中に強調表示されているアウトレットを追加します。追加するアウトレットを記述する場所はインスタン変数を宣言もしくは定義できる { と } の間であればどこでも構いません。しかし一応最後の行に追加することにいたします。
@interface Controller : NSObject {
Model *model;
IBOutlet NSTextView *textView;
IBOutlet NSWindow *window;
IBOutlet NSMatrix *matrix;
}
Controller.m
Controller.mのawakeFromNibメソッドの最後の行にも強調表示されているメッセージ式を追加します。このメッセージ式はtag変数にユーザデフォルトの設定値が代入される式よりもあとに記述しなければならないことはご理解いただけるものだと思います。
-
(void)awakeFromNib
{
NSInteger
tag;
[textView
setString:@""];
if
([model string]) {
[textView
insertText:[model string]];
[window
setDocumentEdited:NO];
[textView
scrollRangeToVisible:NSMakeRange(0, 0)];
}
[window
center];
tag =
[[NSUserDefaults standardUserDefaults]
integerForKey:@"textViewColor"];
[self
setTextViewColor:tag];
[matrix selectCellWithTag:tag];
}
●コード説明
[matrix selectCellWithTag:tag];
マトリックスを構成しているセルのなかから引数tagと同じ整数値のTagを持つセルを選択されている状態にします。
●Interface Builderでの作業
MyClipプロジェクトウィンドウでMainMenu.xibをダブルクリックしてInterface BuilderでMainMenu.xibファイルを開きます。そしてMainMenu.xibウィンドウからPreferencesパネルのMatrixに向かって接続線を引きます。
図matrixアウトレットの接続1 図の大きさは適宜調整のほどお願いいたします。
Matrixが強調表示されましたらマウスを離して現れるOutlets一覧からmatrixをクリックして接続を確定します。
図matrixアウトレットの接続2 図の大きさは適宜調整のほどお願いいたします。
今回のInterface Builderでの作業はアウトレットの接続だけで終わりです。作業を保存してXcodeに戻ってください。
●実行
コンパイルをして再びMyClipを実行してください。そしてPreferencesパネルを開きます。
テキストビューの背景色と同じ色のラジオボタンが選ばれていることだろうと思います。
図MyClip実行画面4
次にMyClipを一度終了させてユーザディレクトリ/ライブラリ/Preferencesフォルダのcom.yourcompany.MyClip.plistを削除してください。
図Preferencesフォルダ
そして再び「ビルドして進行」をクリックしてMyClipを起動します。デフォルトの白い背景色に戻っていると思います。Preferencesパネルも開いてみましょう。こちらもwhiteラジオボタンが選ばれていると思います。
図MyClip実行画面5 図の大きさは適宜調整のほどお願いいたします。
●MyClip.plistファイル
MyClipを終了させてPreferencesフォルダを再び開いてみてください。新たなcom.yourcompany.MyClip.plistファイルが出来上がっていると思います。このファイルをダブルクリックするとProperty List Editorというアプリケーションが起動してMyClip.plistファイルが開きます。
図:MyClip.plist1 図の大きさは適宜調整のほどお願いいたします。
textViewColorというキー(Key)にNumberという型(Type)で0という値(Value)が入っています。0は白の背景色でしたね。Property List Editorを終了して再びMyClipを起動してください。そしてPreferencesパネルを開いて「green」を選択してみましょう。
図MyClip実行画面6 図の大きさは適宜調整のほどお願いいたします。
テキストビューの背景色が変わりましたらMyClipを終了し、com.yourcompany.MyClip.plistを再び開きます。Valueがgreenを表す6に変わっていると思います。
図:MyClip.plist2 図の大きさは適宜調整のほどお願いいたします。
13.
2. 2 ウィンドウの表示位置の保存
アプリケーションを使いやすいものにするためにはウィンドウの表示位置は大事なことです。ウィンドウの位置はユーザがドラッグすることで移動することができます。しかしアプリケーションの再起動後にはユーザが移動した位置に表示される場合とアプリケーションのデフォルトの位置に戻ってしまう場合があります。MyClipでは再起動後もユーザが移動した位置にウィンドウが表示されるようにしたいと思います。
●Interface Builderでの作業
XcodeのプロジェクトウィンドウでMainMenu.xibファイルをダブルクリックしてInterface Builderでxibファイルを開きます。そしてMyClipウィンドウのタイトルバーあたりをクリックするかMainMenu.xibウィンドウでWindow(MyClip)を選択します。選インスペクタパネルのタイトルがWindow ○○になりましたらAttributesタブを選んでください。そして「Autosave」項目に任意の名前を記入します。今回はPositionにいたしました。作業を保存してXcodeに戻ってください。
図:Window Attributes Autosave 図の大きさは適宜調整のほどお願いいたします。
●実行
「ビルドして実行」でMyClipをコンパイルして実行し、そして終了します。「Preferences」フォルダのcom.yourcompany.MyClip.plistファイルをダブルクリックして開きます。今度はメインウィンドウの位置とサイズを表す「Window Frame Position」というキーに文字列(String)の値が追加されています。
図:MyClip.plist 3 図の大きさは適宜調整のほどお願いいたします。
Property List Editorを終了してふたたびMyClipを起動します。そしてウィンドウを移動してMyClipを再起動します。しかしウィンドウの位置はセンターのままです。これはawakeFromNibメソッドの中の
[window center];
というメッセージ式が生きているからです。MyClipを終了してController.mのawakeFromNibメソッドを変更します。
なお、このPreferencesフォルダに出来るアプリケーション識別名.plistファイルはASCII文字列になっています。あとの章でもまた違う目的のplistファイルが出てきますが、そちらはXML形式になっています。XML形式の場合はMacに付属のテキストエディットへドラッグ&ドロップすることで開くことができ編集もできます。しかし現在見ているアプリケーション識別名.plistファイルはテキストエディットで編集することができないわけではありませんが向いてはいないと言えます。
●awakeFromNibメソッドの変更
awakeFromNibメソッドの実装を次のように変更します。強調箇所が変更箇所です。
- (void)awakeFromNib
{
NSUserDefaults *defaults;
NSInteger
tag;
[textView
setString:@""];
if
([model string])
[textView insertText:[model string]];
[window
setDocumentEdited:NO];
[textView
scrollRangeToVisible:NSMakeRange(0, 0)];
/*
UserDefaults の設定を反映させる */
defaults = [NSUserDefaults standardUserDefaults];
tag = [defaults
integerForKey:@"textViewColor"];
[self
setTextViewColor:tag];
[matrix
selectCellWithTag:tag];
if
([defaults stringForKey:@"NSWindow Frame Position"] == nil)
[window
center];
}
●コード説明
1行目
NSUserDefaults *defaults;
NSUserDefaultsクラスのインスタンスを受け入れる変数の宣言をしています。
defaults = [NSUserDefaults standardUserDefaults];
tag = [defaults
integerForKey:@"textViewColor"];
前項では2つのメッセージ式をネストさせて1行にしていましたが、今回はNSUserDefaultsのインスタンスものちほど必要となりますので2行に分けています。
if ([defaults stringForKey:@"NSWindow Frame
Position"] == nil)
[window center];
ユーザデフォルトからキーで識別される文字列を取得するメソッドがstringForKey:です。この値を取得するメソッドは該当するキーがなかった場合にはnilを返します。if文では「NSWindow Frame Position」というキーがなかった場合(nilの場合)
[window center];
が実行されることになります。MyClipを初起動した時にメインウィンドウをデフォルトのセンターから移動しなかった場合は、そのセンターの位置がユーザデフォルトに書き込まれます。したがって次回の起動時にもメインウィンドウは実質的にセンターに表示されることになります。
●実行
コーディングが終わりましたらファイルを保存してMyClipを「ビルドして進行」してみてください。そしてメインウィンドウの位置を移動してアプリケーションを再起動します。アプリケーションを終了する前の位置にウィンドウが表示されることだろうと思います。
13. 2. 3 ユーザデフォルト設定値の破棄
ユーザが個別に設定した値をアプリケーションのデフォルト設定値に戻したい場合もあります。MyClipで言えばメインウィンドウの表示位置を正確にセンターに戻したい場合などが考えられます。この項ではこのユーザデフォルトの破棄について説明したいと思います。
●Controller.h
最後の行(@endの手前)に次のメソッド宣言を加えます。記述場所はメソッド宣言の記述できる場所ならどこでも構いませんが便宜上最後の@endの手前にいたします。
- (IBAction)restoreAllDefaults:(id)sender;
●Controller.m
次にメソッド定義をdeallocメソッドの手前に記述します。この場合もコードを記述する場所に意味はありませんが便宜上deallocメソッドの手前にいたしました。
- (IBAction)restoreAllDefaults:(id)sender
{
NSUserDefaults
*defaults;
defaults
= [NSUserDefaults standardUserDefaults];
[defaults
removeObjectForKey:@"textViewColor"];
[defaults
removeObjectForKey:@"NSWindow Frame Position"];
}
●MainMenu.xib
MainMenu.xibを開いてPreferencesパネルに次の図のように「Restore All Defaults」というボタンを追加します
図Restoreボタンの追加
次にRestore All DefaultsボタンからControllerオブジェクトへ接続線を引きます。
図Restoreボタンの接続1 図の大きさは適宜調整のほどお願いいたします。
そしてrestoreAllDefaults:アクションに接続します。
図:Restoreボタンの接続2 図の大きさは適宜調整のほどお願いいたします。
●実行
Interface Builderでの作業を保存してXcodeに戻ってください。「ビルドして進行」をクリックしてMyClipを実行します。ウィンドウを移動したりText Viewのカラーバリエーションを変更したりしてみましょう。そしてPreferencesパネルのRestore All Defaultsボタンを押します。ボタンを押した効果はすぐには現れません。一度終了してください。
この段階でユーザのホームディレクトリ→「ライブラリ」フォルダ→「Preferences」フォルダを見てもcom.yourcompany.MyClip.plistはすでになくなっています。
MyClipを起動してみましょう。ウィンドウの表示位置はセンターに、Text Viewの背景色は白というMyClipのデフォルト設定に戻っています。
お疲れ様でした。これで第13章を終わります。
This site is available in Safari and Snow Leopard. | (c) viva Cocoa 2006 - 2010 |