Cocoaはやっぱり!
Mac OS X 10.3 Pantherの新機能

今回のテーマ

今回のテーマは、「 Mac OS X 10.3 Pantherの新機能 」です。Mac OS X 10.3に新たに追加された主な機能や変更点について紹介をしていきます。変更点が多いので、このドキュメント自身、今後何度か更新する予定です。なお、現時点では、全機能を実際に試しているわけではありませんのでご了承ください。

推奨環境 この解説は、以下の環境を前提に作成し、動作確認等を行っています。ご確認ください。

改版履歴

NSView #1

デザインの変更とミニサイズの追加 : いくつかのビューはデザインが変更され、多くのビューについて、ミニサイズ版というコンパクトなものが追加されました。 setControlSize : メソッドで変更、 controlSize でサイズの取得ができます。値は「 NSRegularControlSize、NSSmallControlSize、 NSMiniControlSize 」です。Interface Builderからも設定できます。

フォーカスリング表示の制御 : フォーカスリングの表示スタイルを各ビュー毎に変更することができるようになりました。フォーカスリングを付けたくないビューは、これで対応が出来ます。 setFocusRingType : メソッドで変更が出来ます。

NSView : ビューのフォーカスリングのスタイルを変更する
書式 : - (void) setFocusRingType : (NSFocusRIngType) type 入力 : type - 変更したいフォーカスリングのスタイル
NSFocusRingType : ビューのフォーカスリングのスタイルの定数
typedef enum { NSFocusRingTypeDefault, // デフォルトのスタイル NSFocusRingTypeNone, // フォーカスリングなし NSFocusRingTypeExterior // フォーカスリングあり } NSFocusRingType;

最描画の最適化 : 変更のあった領域 ( Dirty Rectangle ) の管理を行えるようになりました。これによって、描画の最適化を行うことが出来、高速化が実現できます。

滑らかなスクロール : スクロールが滑らかになりました。

アニメーションの高速化 : ビューのアニメーションが高速になりました。

NSView #2 : ビューを隠す

NSViewの変更点の中で、ある種一番期待されていたともいえるのが、「 ビューの表示/非表示制御 」ではないでしょうか。CocoaのFAQの1つとして「 NSViewを隠す方法は? 」というのがあると思うのですが、今までNSViewを隠す直接的な方法がありませんでした。でも、もうこれからは悩む必要はありません。そのものずばりの setHidden : isHidden というメソッドが追加されました。

しかし、若干の注意が必要です。子供を持つビューを隠すと、その子供のビューも連動して隠れるという連鎖が発生するためです。「 自分自身が非表示か、もしくは自分の全ての親の中で1つでも非表示のものがある場合は画面には表示されない 」というルールになっています。isHidden メソッドは、対象のビュー自身の属性の値を返すだけですので、YESが返ってきても、親が非表示の場合は画面には表示されていません。親も含めてチェックしてくれるメソッドも用意されていて、 isHiddenOrHasHiddenAncestor メソッドがそれです。属性をチェックしたいなら isHidden、画面に表示されているかをチェックしたいなら isHiddenOrHasHiddenAncestor という風に使い分けます。

NSView : 表示/非表示属性を変更する
書式 : - (void) setHidden : (BOOL) flag 入力 : flag - 表示するならYES、隠すならNO
NSView : ビュー自身の表示/非表示属性を取得する
書式 : - (BOOL) isHidden 出力 : 返り値 - 表示(=YES)、非表示(=NO) 備考 : このメソッドの返り値は、このビュー自身の属性値であって、YESであっても親のビューが非表示であれば、このビューも画面には表示されない。表示されているかどうかはisHiddenOrHasHiddenAncestorで取得できる。
NSView : ビューの表示/非表示状態を取得する
書式 : - (BOOL) isHiddenOrHasHiddenAncestor 出力 : 返り値 - 表示(=YES)、非表示(=NO) 備考 : このメソッドの返り値は、このビューが画面に表示されているかの状態を返す。属性値はisHiddenで取得できる。

この非表示状態になると、レスポンダーチェインの連結状態も変わりますので、隠されているビューにイベント処理がレスポンダーチェインの流れではやってこなくなります。

1つ気をつけなければならないのは、非表示状態の使い方です。使うのは簡単ですが、その使い方が Human Interface Guidelines に沿っているかどうか。ユーザインターフェイスの要素を状況に応じて表示したり隠したりというのは、必要最小限にした方がよいのです。ボタンなどを隠してしまうと「 あれ?あのボタンどこかにあったんだけどなぁ 」とあちこちユーザが探しまわってしまうことになります。ですので、通常は「 使用不可の状態にして存在場所を常に明らかにしておく 」のが一般的です。正しく使いましょう。

NSButton

ディスクロージャトライアングル : NSButtonにディスクロージャトライアングルのスタイルが追加されました。あるときは詳細設定まで行わせ、あるときはシンプルな画面を実現するためによく使われるものです。 setBezelStyle : メソッドに NSDisclosureBezelStyle を指定します。Interface Builderからも設定できます。

テクスチャードボタン : iTunesのようなテクスチャードウィンドウによく似合うデザインのボタンが標準でサポートされるようになりました。 setBezelStyle : メソッドに NSTexturedSquareBezelStyle を指定します。Interface Builderからも設定できます。

NSSlider : 回転スライダー

オーディオ機器のような回転式のつまみのスタイルがNSSliderに追加されました。設置面積が狭いのが特徴です。 setSliderType : メソッドでスライダーのタイプを NSCircularSlider に変更します。通常のスライダーのタイプは NSLinearSlider です。現在のタイプを取得するには、 sliderType メソッドを用います。

NSSlider : スライダーのスタイルを変更する
書式 : - (void) setSliderType : (NSSliderType) sliderType 入力 : type - 変更したいスライダーのスタイル
NSSlider : スライダーのスタイルを取得する
書式 : - (NSSliderType) sliderType 出力 : 返り値 - スライダーのスタイル
NSSliderType : スライダーのスタイルの定数
typedef enum { NSLinearSlider, // 棒状のスライダー NSCircularSlider // 回転式のスライダー } NSSliderType;
NSSegmentedControl : セグメンテッドコントロール

Finderの表示モードの切り替えなどで使われている、ラジオボタンのように振舞う連結したプッシュボタンが標準で用意されるようになりました。NSControlのサブクラスです。Interface Builder 2.4 (v349) ではサポートは行われていないため、他のボタンのようにすぐに使うことはできません。若干面倒ですが、以下のようにすると使うことは可能です。

まず、XCodeでNSSegmentedControlを検索してヘッダーファイルを開きます。そのヘッダーをInterface Builder上で開いているnibファイルのウィンドウにドロップしてNSSegmentedControlの定義を覚えさせます。そして、CustomViewをウィンドウなどに配置しまして、InfoウィンドウのCustom ClassからクラスをNSSegmentedControlに変更します。これで、NSSegmentedContorlがウィンドウ上に配置されたことになります。

NSSegmentedControl独自の設定はInterface Bulder上からはできませんので、コード上で行います。以下のサンプルは、vSegというアウトレットにNSSegmentedControlをつないだ状態でのものです。 setSegmentCount : でセグメントの個数を指定します。各セグメントに画像を貼付けるなら setImage : forSegment : 、ラベルを付けるなら setLabel : forSegment : を使います。forSegment : の引数は、セグメントのインデックスで、一番左のセグメントが0になります。

NSSegmentedControl : サンプル
[ vSeg setSegmentCount : 3 ]; // 3つのセグメントを持たせる [ vSeg setImage : [ NSImage imageNamed: @"sleep_1" ] forSegment : 0 ]; [ vSeg setImage : [ NSImage imageNamed: @"sleep_2" ] forSegment : 1 ]; [ vSeg setImage : [ NSImage imageNamed: @"sleep_3" ] forSegment : 2 ]; [ vSeg setLabel : @"Kero-1" forSegment : 0 ]; [ vSeg setLabel : @"Kero-2" forSegment : 1 ]; [ vSeg setLabel : @"Kero-3" forSegment : 2 ];

NSImageView

アニメーション表示 : 現在はアニメGIFのみ対応ですが、アニメーション付きの画像をアニメーションとして表示できるようになりました。 setAnimates : メソッドでYESをセットするとアニメーション表示されるようになります。状態は、 animates メソッドで取得できます。フレームの切り替わりのタイミングやループ再生するかどうかというのは、画像データに設定されている情報に従います。

試してみるには、Interface Builderで「 Editable 」と「 Animates 」にチェックを入れて「 Test Interface 」メニューで実行してみて、NSImageViewにアニメーションGIFのファイルをドロップしてみてください。これだけでアニメーション表示されます。

NSTextFieldCell

プレイスホルダー : ユーザが文字列を入力する前の空っぽの状態のときに表示する文字列を指定できるようになりました。Interface Builder上でも指定できますし、 setPlaceholderString : メソッドで変更、 placeholderString メソッドで取得できます。修飾付きの文字列にすることもできます。 setPlaceholderAttributedString placeholderAttributedString を使用します。

NSTextView

テキストの補完機能 : NSTextFieldやNSTextViewには、テキストの補完機能 ( Completion ) が搭載されました。単語を途中まで入力したところで、単語の候補が表示されてそこから入力できるというものです。これには学習機能も付いており、最近選択したものが候補の上に来たり、候補に無かったものをタイプした場合は、それが記憶されて次回は候補の中に現れます。

ルーラ強化 : NSTextViewのルーラは、より使いやすくするために多くの改善が行われています。よく使う文字修飾情報を登録することも出来ます。登録した情報は、プリファレンスファイルに記録されるようになります。

検索パネル : 検索を行う時に使用するパネルが標準のクラスとして装備され、NSTextViewからもコーディングなしで利用できます。Interface Builder上では「 Uses Find Panel 」をチェックするだけです。

Microsoft Word書類のサポート : フルスペックのサポートではなく、テーブルなどまでは再現できませんが、Word書類を読み込むことができるようになりました。

スタイルの選択用シート : フォントメニューから「 スタイル... 」を選択すると、登録しているスタイルを選択するシートを表示するようになりました。

NSSearchField : 検索用テキストフィールド

SafariやMail、iTunesなどで用いられている検索テキストを入力するためのテキストフィールドが提供されるようになりました。NSTextFieldのサブクラスです。左側にあるルーペの部分にメニューをつけることができます。searchMenuTempleateというアウトレットが用意されていますので、Interface Builder上でメニューを作ってコネクトするだけでも利用可能です。メソッドは、 setSearchMenuTemplate : searchMenuTemplate があります。

NSTableView

行の背景色 : 行の背景色を交互に変更できるようになりました。Interface Builderからも設定できます。

セルの変更 : Interface Builder上で、各カラムに使用するセルを簡単に指定できるようになりました。パレットからカラムにドロップするだけで、テーブル内にチェックボックスをつけたり、スライダーをつけたりできます。NSOutlineViewでも同様です。

NSMenu, NSMenuItem

サブメニュー付きのメニューのアクション : 今までは、サブメニューを持っているメニューを選択しても何も実行されませんでしたが、アクションとターゲットを設定できるようになりました。

ダイナミックメニュー : Carbonでは実現できていましたが、シフトキーなどのモディファイキーを押した時にメニューが切り替わるようなダイナミックなメニューの動きを可能にしました。どのようにするかをInterface Builderでの設定でみてみましょう。

Fileメニューの中に、「 New 」と「 New As Rich Text 」というメニュー項目があります。「 New 」が通常表示されるメニューで、「 New As Rich Text 」は通常表示されず、オプションキーが押されたときに「 New 」に置き換わるように指定することができます。このときの「 New As Rich Text 」の方を Alternate Menu と呼びます。「 Treat as an alternate to the previous item 」にチェックをつけると、直前のメニュー項目のAlternate Menuになります。この場合、コマンドキーにチェックをし、Key Equivalentとシフトキーのチェックを同じにしておく必要があります。

プログラム上では、 setAlternate : メソッドを使ってAlternate Menuを指定します。状態取得は、 isAlternate メソッドを使います。

メニューのツールチップ : メニューにもツールチップをつけることが可能になりました。Interface Builder上からも指定できますし、 setToolTip : で変更、 toolTip で取得も可能です。

NSToolbar

システム環境設定などでも使用されていますが、ツールバー上のアイテムを選択状態にするための仕掛けが追加されました。選択状態を許すNSToolbarItemの配列を返すメソッド toolbarSelectableItemIdentifiers : をNSToolbarのデリゲート側に実装しておきます。選択アイテムの変更が setSelectedItemIdentifier : selectedItemIdentifier で現在の選択アイテムが取得できます。

Interface Builder上で、ツールバーを作成できるようなものを期待していましたが、今回もありませんでした。

NSWindow

非アクティブアプリケーションのウィンドウ上のアイテムのツールチップの表示を可能にできるように拡張が行われました。Interface Builder上からも設定できます。

NSWindow : アプリケーションが非アクティブのときにもツールチップを表示するかを変更する
書式 : - (void) setAllowsToolTipsWhenApplicationIsInactive : (BOOL) flag 入力 : flag - ツールチップを表示する(=YES)、しない(=NO)
NSWindow : アプリケーションが非アクティブのときにもツールチップを表示するかを取得する
書式 : - (BOOL) allowsToolTipsWhenApplicationIsInactive 出力 : 返り値 - ツールチップを表示する(=YES)、しない(=NO)
NSFontPanel

設定可能項目の変更 : フォントパネルは、今まで、フォントファミリー/スタイル/サイズの3つを必ず指定するようになっていましたが、「 フォントサイズはしていしなくてもいい 」ような場合もあります。そこで、特定の項目だけを指定できないようにしたりということができるようになりました。フォントパネルがchangeFont : を送るレスポンダー側に validModesForFontPanel : メソッドを実装しておき、このメソッドがフォントパネルに表示する項目を返します。

フォントパネルの文字サイズの変更を消す
- (unsigned int) validModesForFontPanel : (NSFontPanel *) fontPanel { return( NSFontPanelAllModesMask - NSFontPanelSizeModeMask ); }
フォントパネルに表示する項目の定数定義
enum { NSFontPanelFaceModeMask = 1 << 0, NSFontPanelSizeModeMask = 1 << 1, NSFontPanelCollectionModeMask = 1 << 2, NSFontPanelStandardModesMask = 0xFFFF, NSFontPanelAllModesMask = 0xFFFFFFFF };

設定項目の増強 : フォントパネルには、文字色の指定やアンダーライン、打ち消し線、ドロップシャドウなどの設定が増えました。

NSFontManager : フォントコレクションへのアクセス

フォントパネルの機能に、よく使うフォントのセットを管理するための「 フォントコレクション 」があります。このフォントコレクションにアクセスする手段がNSFontManagerによって提供されるようになりました。

collectionNames で、全コレクションのリストを取得、 addCollection : options : で空のコレクションを追加、 removeCollection で削除できます。

NSOpenPanel, NSSavePanel

フォルダ作成ボタン : ファイルオープンパネルにもフォルダ作成ボタンを追加できるようになりました。 setCanCreateDirectories : メソッドで変更、 canCreateDirectories メソッドで取得できます。

NSSavePanel : フォルダ作成ボタンを付けるかどうかを変更する
書式 : - (void) setCanCreateDirectories : (BOOL) flag 入力 : flag - フォルダ作成ボタン付ける(=YES)、付けない(=NO)
NSSavePanel : フォルダ作成ボタンを付けるかどうかの状態を取得する
書式 : - (BOOL) canCreateDirectories 出力 : 返り値 - フォルダ作成ボタンが付いている(=YES)、付いていない(=NO)
NSAlert

従来は、アラート表示のためにC言語スタイルのAPIが提供されていましたが、Objective-Cのクラスとしてアラートが提供されるようになりました。単にスタイルが変わっただけでなく、「 アラートの中のアイコンを変更 」「 ボタンを3つ以上配置する 」などの詳細な設定も可能になっています。

アラートをモーダルに表示
// アラートを作成 NSAlert *alert = [ NSAlert alertWithMessageText : @"Message" defaultButton : @"Default Button" alternateButton : @"Alternate Button" otherButton : @"Other Button" informativeTextWithFormat : @"Informative Text" ]; // アラートを表示 int iHitButton = [ alert runModal ]; // 結果により分岐 switch( iHitButton ) { case NSAlertDefaultReturn : NSLog( @"Default" ); break; case NSAlertAlternateReturn : NSLog( @"Alternate" ); break; case NSAlertOtherReturn : NSLog( @"Other" ); break; }

NSApplication

アプリケーションに対しての複数ファイルオープンと印刷 : 従来、アプリケーションのアイコンにファイルが複数個ドロップされた時、「ファイルがドロップされたよ」という通知がファイルの個数分飛んできていました。ただし、これでは、経過表示を行いたい場合に困ります。ドロップされた数が分からないのですから。これが、ファイルの配列で通知されるようになります。ファイルが開かれる時は、 application : openFiles : が呼ばれ、ファイルが印刷される時は、 application : printFiles : が呼ばれます。

NSImage

対応画像フォーマット : PostscriptとEPSに対応しました。また、CMYKのカラースペースにも対応しています。

NSCursor

カーソルサイズの制限撤廃 : 16×16ドットよりも大きなカーソルを作成できるようになりました。

標準カーソルの生成 : システムに標準で持っているカーソルを生成する種類が増えました。

NSCursor : 標準カーソル生成クラスメソッド
+ (NSCursor *) pointingHandCursor; + (NSCursor *) closedHandCursor; + (NSCursor *) openHandCursor; + (NSCursor *) resizeLeftCursor; + (NSCursor *) resizeRightCursor; + (NSCursor *) resizeLeftRightCursor; + (NSCursor *) resizeUpCursor; + (NSCursor *) resizeDownCursor; + (NSCursor *) resizeUpDownCursor; + (NSCursor *) crosshairCursor;
NSWorkspace

スリープの通知 : スリープする直前や、スリープから復帰した直後に通知を受け取ることができるようになりました。

NSSpeechSynthesizer

従来は、NSTextViewにテキストの読み上げ機能があったため、隠したNSTextViewを作っておいて speech : メソッドを実行するなどしていましたが、専用のクラスができました。

音声の種類 : defaultVoice でデフォルトの音声の名前、 availableVoices メソッドで利用可能な音声の名前が取得できます。取得した名前で発声するためには initWithVoice : メソッドで初期化します。

デリゲートによるユーザフィードバック : 発声する直前に、その発音記号発声する単語の位置がデリゲートに通知されます。発音記号によってキャラクターの口の形を変えたり、しゃべっているテキストを画面に表示している場合は、その場所をハイライト表示させたりできるわけです。

音声ファイルへの出力 : startSpeakingString: toURL: メソッドで、AIFFファイルへ出力ができます。

適当なクラスに以下のコードを入れて、ボタンなどに speech : メソッドをつないで動かしてみてください。

NSSpeechSynthesizer : サンプル
- (IBAction) speech : (id) sender { // デフォルトボイスを取得 NSString *voice = [ NSSpeechSynthesizer defaultVoice ]; // 初期化 NSSpeechSynthesizer *speech = [ [ NSSpeechSynthesizer alloc ] initWithVoice : voice ]; // AIFFへ出力 NSURL *url = [ NSURL fileURLWithPath : [ @"/Users/crane/speech.aiff" stringByExpandingTildeInPath ] ]; [ speech startSpeakingString : @"Hello Panther" toURL : url ]; // 自分自身をデリゲートを設定 [ speech setDelegate : self ]; // しゃべる [ speech startSpeakingString : @"Hello Panther" ]; } - (void) speechSynthesizer : (NSSpeechSynthesizer *) sender // このシンセサイザーは didFinishSpeaking : (BOOL ) finishedSpeaking { // しゃべり終わったよ NSLog( @"didFinishSpeaking" ); } - (void) speechSynthesizer : (NSSpeechSynthesizer *) sender // このシンセサイザーが willSpeakPhoneme : (short ) phonemeOpcode { // この発音記号をしゃべるよ NSLog( @"%d", phonemeOpcode ); } - (void) speechSynthesizer : (NSSpeechSynthesizer *) sender // このシンセサイザーが willSpeakWord : (NSRange ) characterRange // この範囲をしゃべるよ ofString : (NSString *) string { // この文字列の NSLog( @"%@", [ string substringWithRange : characterRange ] ); }
NSController

GUIアプリケーションは、Model-View-Controllerの3つのオブジェクトで構成することで、それぞれの非依存性を高くでき、そのために再利用性の高いクラスが作成できることが知られています。今まで、Viewに関しては、NSViewをはじめとするクラスで提供され、Modelに関しては、Foundationによって多くのクラスが提供されていました。Controllerについてはサポートがなかったため、再利用性の高いコードを書くためには各プログラマでの工夫が求められていましたが、 NSController クラスとして提供されるようになりました。

このクラスについて解説すると長くなるため、1つメリットのわかりやすい例についてピックアップしておきます。

いくつかNSControllerのサブクラスも提供されており、その1つが NSUserDefaultsController です。このNSUserDefaultsControllerを使うことで、コードを一切書かなくとも、環境設定ウィンドウのビューの状態を環境設定ファイルへ保存し、次回起動時にはビューにセットすることができます。実際に行ってみましょう。

Cocoaのアプリケーションのプロジェクトを作成し、Interface BuilderでMainMenu.nibを開きます。以下のように、NSTextField、NSButton、NSPopupButtonを配置します。NSTextFieldを選択して、Infoウィンドウを表示させます。Infoウィンドウに Bindings というペインが追加されていますので、上部のメニューで切り替えます。

Bindingsというのは、インスタンスの値を他のインスタンスと結びつける(Bindする)という意味です。今回のケースでいうと、「 NSTextFieldに入力した文字列のインスタンスを初期設定のインスタンスを結びつける 」という風に考えておけばよいでしょう。Bindingsペインを開くと、NSTextFieldが持っているどのインスタンスを結びつけるかを選ぶことができます。NSTextFieldに入力した文字列ならばValueになりますので、Valueのところを開きます。そして、Bindのチェックボックスをオンにします。これでバインドされます。

どのインスタンスにバインドするかを Bind to で選びますが、 Shared User Defaults にしておきます。Shared User Defaultsというのは、NSUserDefaultsControllerのインスタンスになります。そして、 Model Key Path のところに「 profileName 」と入力します。この文字列は、環境設定ファイルの中に値を書き込む際のキーとして使われます。

チェックボックスについては全く同様の操作でバインドできます。Model Key Pathは「 profileMacUser 」とします。ポップアップメニューは、選ばれているメニューの番号を環境設定に保存したい場合ならば、selectedIndexのペインを開いてバインドの設定を行うことになります。Model Key Pathは「 profileBlood 」としました。

ここまで行いましたら、ビルドして実行してみてください。ウィンドの中を操作して終了し、再度起動するとビューの中身が終了時の状態に復帰されているはずです。NSControllerによってインスタンスのバインドが実現されて、インスタンス間の値を同期させることができるようになり、さらに、NSUserDefaultsControllerによって、初期設定ファイルとのやりとりも自動化されるというような仕組みになっています。

環境設定ファイルの中身も確認してみましょう。以下のようになっています。

この例は、NSControllerの一部の機能についての説明にしかなっていませんし、M-V-CのMも出てきていない例でしたが、インスタンス間の連携をスマートにするものとして理解していただければよいかと思います。

WebView

WebViewがInterface Builder上でサポートされました。WebViewの各種パラメータを設定することができます。

Objective-C

Objective-Cに、待望の例外処理のハンドリングの機能が付きました。Javaスタイルの try 〜 catch の記述になります。バイナリには互換性があります。

例外の処理
@try { ... return( object ); } @catch (NSException *) e { ... }
その他

NSXMLParser : XMLパーサークラスが追加されました。

NSNib : Nibファイルをメモリに置いたりとか、高速化などの手段を提供します。

NSStreams : ストリームによるファイルアクセス。

NSHelpManager : ヘルプファイルへのアクセス方法の拡張が行われました。

XCode

XCodeヘルプ : XCodeを起動して、「 ヘルプ → XCodeヘルプ 」メニューを選択すると開発資料の閲覧をするためのビューが起動します。ツールバー上の検索フィールドに文字列を入力すると、検索結果がリアルタイムにフィードバックされます。リファレンスを引くのに重宝します。

コード入力補助 : XCodeでソースコード入力中に「 FunctionとF5 」を押すと、入力途中のクラス名などなどの候補が表示され、そこから入力できます。また、キーアサインは環境設定で細かく変更できますので、自分の好きなキーに割り当てることが可能です。

コード入力補助の設定 : コード入力補助については、いくつか設定があり、入力しているだけで候補が表示されるようにするというようにもできます。邪魔と感じるか便利と感じるかは人それぞれかと思いますが。