コードなしで動かしてみる - Outlets, Target/Action 体験
- Shift+Command+N で新規プロジェクト、Core Data Application、プロジェクト名を「SpeechClock」として「完了」。
- SpeechClock プロジェクトで「MainMenu.nib」をダブルクリックしてインターフェースビルダー(IB)で開く。
- Instances の「Window」を delete。かわりに、Cocoa Windows の「NSWindow and NSDrawer」を Instances へ。すると、NSDrawer1 と Parent Window と DrawContentView というインスタンスが作られる。
- Parent Window のタイトルを Command+1 で「SpeechClock」に変更。
- Cocoa Controls の NSButton を「SpeechClock」Window へ。NSButton のタイトルは「Voice」とする。Command+1 で「Visible at launch time」をオンにしておく。
- Voice ボタンから Control を押しながら「NSDrawer1」へ「toggle:」Action で Connect。
- ウィンドウが狭いと作り難いので、とりあえずサイズを 480x360 ぐらいに広くしておこう。
- 「NSDrawer1」の Preferred Edge を Command+1 で「Bottom」にしておく。
- ここで、IB 上で、Command+R で試験。Voice ボタンで NSDrawer ウィンドウが出現すれば成功。
データのためのテーブルを配置してみる - Core Data とレイアウトの体験
- SpeechClock プロジェクトで「SpeechClock_DataModel.xcdatamodel」を開き、エンティティ「voices」(クラスは規定の NSManagedObject)を作成。属性は「voice、文字列」とする。
- 次の操作に備え、MainMenu.nib を IB で開いておく。
- voices エンティティを Option を押しながら、「DrawContentView」Windowへ。さまざまな Cocoa Controls が生成される。
- 生成された NSTableView について「Use Alternating Raw Background」「Automatically Hide Scrollers」を有効にした。
- 生成されたさまざまな部品の配置はなるべくコンパクトにまとめ、NSDrawer1 の Command+3 のサイズで最小/最大サイズ等を調整するとよい。
- それぞれの部品の自動サイズを調整し、意図するレイアウトになるまで Command+R で試しながら調整するとよい。
コードを書いて動かしてみる - Outlet の作成と Connection を体験
- SpeechClock プロジェクトで「SpeechClock_AppDelegate.h」と「SpeechClock_AppDelegate.m」をリスト1のように編集する。
- Classes の「SpeechClock_AppDelegate」(インスタンスをダブルクリックするとクラスへ)の Command+1 で outlet として「sampleTextField」、action として「startSpeechSampleText」と「stopSpeechSampleText」を追加する。
- NSBox を「DrawContentView」Windowへ。タイトルは「Speech」とする。さらに、NSTextField と2つの NSButton を「Speech」Box へ。NSTextField のタイトルは適当なゼリフ「Well begun is half done.」、NSButton のタイトルは配置の右から「Speak」「Stop」とする。
- speech「Speak」Button から SpeechClock_AppDelegate の「startSpeechSampleText」へ Connection。
- speech「Stop」Button から SpeechClock_AppDelegate の「stopSpeechSampleText」へ Connection。
- SpeechClock_AppDelegate の「sampleTextField」outlet から speech NSTextField へ Connection。
- ここで、Xcode上で、Command+R で試験。Speak ボタンを押して声がして、Stop ボタンで途中で止められれば成功。
リスト1
--- SpeechClock_AppDelegate.h~01 2010-04-09 12:49:38.000000000 +0900 +++ SpeechClock_AppDelegate.h 2010-04-09 14:58:15.000000000 +0900 @@ -11,6 +11,8 @@ @interface SpeechClock_AppDelegate : NSObject { IBOutlet NSWindow *window; + IBOutlet NSTextField *sampleTextField; + NSSpeechSynthesizer *speechSynthesizer; NSPersistentStoreCoordinator *persistentStoreCoordinator; NSManagedObjectModel *managedObjectModel; @@ -23,4 +25,7 @@ - (IBAction)saveAction:sender; +- (IBAction)startSpeechSampleText:(id)sender; +- (IBAction)stopSpeechSampleText:(id)sender; + @end --- SpeechClock_AppDelegate.m~01 2010-04-09 12:49:38.000000000 +0900 +++ SpeechClock_AppDelegate.m 2010-04-09 14:59:18.000000000 +0900 @@ -10,6 +10,23 @@ @implementation SpeechClock_AppDelegate +- (id)init +{ + [super init]; + speechSynthesizer = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; + return self; +} +- (IBAction)startSpeechSampleText:(id)sender +{ + NSString *string; + if (![(string = [sampleTextField stringValue]) length]) + return; + [speechSynthesizer startSpeakingString:string]; +} +- (IBAction)stopSpeechSampleText:(id)sender +{ + [speechSynthesizer stopSpeaking]; +}
部品の状態を制御する - delegate Outlet の作成、awakeFromNib の利用を体験
- 「SpeechClock_AppDelegate.h」と「SpeechClock_AppDelegate.m」をリスト2のように編集する。
- Classes の「SpeechClock_AppDelegate」の Command+1 で outlet として「startSpeechSampleTextButton」と「stopSpeechSampleTextButton」を追加する。
- SpeechClock_AppDelegate の「startSpeechTextField」outlet から「Speak」Button へConnection。
- SpeechClock_AppDelegate の「stopSpeechTextField」outlet から「Stop」Button へConnection。
- ここで、Xcode上で、Command+R で試験。起動時に Stop ボタンは無効化されていて、Speak ボタンを押して声がしている間は「Speak」Button が無効化され、声が終われば元に戻れば成功。
リスト2
--- SpeechClock_AppDelegate.h~02 2010-04-09 14:58:15.000000000 +0900 +++ SpeechClock_AppDelegate.h 2010-04-09 15:10:10.000000000 +0900 @@ -13,6 +13,8 @@ IBOutlet NSWindow *window; IBOutlet NSTextField *sampleTextField; NSSpeechSynthesizer *speechSynthesizer; + IBOutlet NSButton *startSpeechSampleTextButton; + IBOutlet NSButton *stopSpeechSampleTextButton; NSPersistentStoreCoordinator *persistentStoreCoordinator; NSManagedObjectModel *managedObjectModel; --- SpeechClock_AppDelegate.m~02 2010-04-09 14:59:18.000000000 +0900 +++ SpeechClock_AppDelegate.m 2010-04-09 15:16:44.000000000 +0900 @@ -14,20 +14,35 @@ { [super init]; speechSynthesizer = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; + [speechSynthesizer setDelegate:self]; return self; } +- (void)awakeFromNib +{ + [startSpeechSampleTextButton setEnabled:!NO]; + [stopSpeechSampleTextButton setEnabled:!YES]; +} - (IBAction)startSpeechSampleText:(id)sender { NSString *string; if (![(string = [sampleTextField stringValue]) length]) return; [speechSynthesizer startSpeakingString:string]; + [startSpeechSampleTextButton setEnabled:NO]; + [stopSpeechSampleTextButton setEnabled:YES]; } - (IBAction)stopSpeechSampleText:(id)sender { [speechSynthesizer stopSpeaking]; } +- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender + didFinishSpeaking:(BOOL)success +{ + [startSpeechSampleTextButton setEnabled:!NO]; + [stopSpeechSampleTextButton setEnabled:!YES]; +} +
データの状態を取得する - NSArrayController の利用を体験
- 「SpeechClock_AppDelegate.h」と「SpeechClock_AppDelegate.m」をリスト3のように編集する。
- Classes の「SpeechClock_AppDelegate」の Command+1 で outlet として「arrayController」を追加する。
- SpeechClock_AppDelegate の「arrayController」outlet から「Voices Array Controller」へConnection。
- ここで、Xcode上で、Command+R で試験。NSTableView に例えば「com.apple.speech.synthesis.voice.Agnes」などを入力してみて、声色が変更できれば成功。
- さらに、正常終了させた後、改めて実行した時に、前に入力したデータが保存されていれば成功。
リスト3
--- SpeechClock_AppDelegate.h~03 2010-04-09 15:10:10.000000000 +0900 +++ SpeechClock_AppDelegate.h 2010-04-09 15:30:33.000000000 +0900 @@ -15,6 +15,7 @@ NSSpeechSynthesizer *speechSynthesizer; IBOutlet NSButton *startSpeechSampleTextButton; IBOutlet NSButton *stopSpeechSampleTextButton; + IBOutlet NSArrayController *voicesArrayController; NSPersistentStoreCoordinator *persistentStoreCoordinator; NSManagedObjectModel *managedObjectModel; --- SpeechClock_AppDelegate.m~03 2010-04-09 15:16:44.000000000 +0900 +++ SpeechClock_AppDelegate.m 2010-04-09 15:32:56.000000000 +0900 @@ -27,6 +27,10 @@ NSString *string; if (![(string = [sampleTextField stringValue]) length]) return; + NSString *voice = ([[voicesArrayController selectionIndexes] count] == 1) ? + [voicesArrayController valueForKeyPath:@"selection.voice"] : + [NSSpeechSynthesizer defaultVoice]; + [speechSynthesizer setVoice:voice]; [speechSynthesizer startSpeakingString:string]; [startSpeechSampleTextButton setEnabled:NO]; [stopSpeechSampleTextButton setEnabled:YES];
データの状態を制御する - NSManagedObject の利用を体験
- 「SpeechClock_AppDelegate.h」と「SpeechClock_AppDelegate.m」をリスト4のように編集する。
- NSButton を「Speech」Box へ1つ追加。NSButton のタイトルは「default」とする。
- Classes の「SpeechClock_AppDelegate」の Command+1 で action として「setVoicesByDefault」を追加する。
- speech「default」Button から SpeechClock_AppDelegate の「setVoicesByDefault」へ Connection。
- ここで、Xcode上で、Command+R で試験。default ボタンでシステム標準の声色リストが NSTableView に揃えられ、選択した声色に変更できれば成功。
- さらに、正常終了させた後、改めて実行した時に、前に入力したデータが保存されていれば成功。
リスト4
--- SpeechClock_AppDelegate.h~04 2010-04-09 15:30:33.000000000 +0900 +++ SpeechClock_AppDelegate.h 2010-04-09 16:03:43.000000000 +0900 @@ -30,5 +30,6 @@ - (IBAction)startSpeechSampleText:(id)sender; - (IBAction)stopSpeechSampleText:(id)sender; +- (IBAction)setVoicesByDefault:(id)sender; @end --- SpeechClock_AppDelegate.m~04 2010-04-09 15:32:56.000000000 +0900 +++ SpeechClock_AppDelegate.m 2010-04-09 16:40:41.000000000 +0900 @@ -39,6 +39,22 @@ { [speechSynthesizer stopSpeaking]; } +- (IBAction)setVoicesByDefault:(id)sender +{ + NSArray *voices = [NSSpeechSynthesizer availableVoices]; + int i; + //[voicesArrayController willChangeValueForKey:@"voice"]; // unnecessary + [voicesArrayController setFilterPredicate:nil]; + [voicesArrayController removeObjects:[voicesArrayController arrangedObjects]]; + for (i=0; i<[voices count]; i++) { + NSManagedObject *voice = + [NSEntityDescription insertNewObjectForEntityForName:@"voices" + inManagedObjectContext:[self managedObjectContext]]; + [voice setValue:[voices objectAtIndex:i] forKey:@"voice"]; + [voicesArrayController addObject:voice]; + } + //[voicesArrayController didChangeValueForKey:@"voice"]; // unnecessary +} - (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)success
時計を表示 - キーバリューコーディング(KVC)とバインド、NSTimerとセレクタを体験
- 「SpeechClock_AppDelegate.h」と「SpeechClock_AppDelegate.m」をリスト5のように編集する。
- NSBox を Parent Window である「SpeechClock」Windowへ。タイトルは「Clock」とする。さらに、NSTextField を「Clock」Box へ。NSTextField のタイトルは「clock」とする。
- clock TextField の value バインドを Command+4 で「SpeechClock_AppDelegate、Controller Key はなし、Model Key Path は currentDate」とする。
- ここで、Xcode上で、Command+R で試験。clock TextField が時計として動いていれば成功。
リスト5
--- SpeechClock_AppDelegate.h~05 2010-04-09 16:03:43.000000000 +0900 +++ SpeechClock_AppDelegate.h 2010-04-09 21:55:22.000000000 +0900 @@ -16,6 +16,8 @@ IBOutlet NSButton *startSpeechSampleTextButton; IBOutlet NSButton *stopSpeechSampleTextButton; IBOutlet NSArrayController *voicesArrayController; + NSDate *currentDate; + NSTimer *clockTimer; NSPersistentStoreCoordinator *persistentStoreCoordinator; NSManagedObjectModel *managedObjectModel; @@ -32,4 +34,12 @@ - (IBAction)stopSpeechSampleText:(id)sender; - (IBAction)setVoicesByDefault:(id)sender; +#if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +- (NSDate *)currentDate; +- (void)setCurrentDate:(NSDate *)aCurrentDate; +#else +@property(readwrite, retain) NSDate *currentDate; +#endif +- (void)updateCurrentDate; + @end --- SpeechClock_AppDelegate.m~05 2010-04-09 16:40:41.000000000 +0900 +++ SpeechClock_AppDelegate.m 2010-04-09 21:56:09.000000000 +0900 @@ -15,12 +15,18 @@ [super init]; speechSynthesizer = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; [speechSynthesizer setDelegate:self]; + currentDate = [[NSDate alloc] init]; return self; } - (void)awakeFromNib { [startSpeechSampleTextButton setEnabled:!NO]; [stopSpeechSampleTextButton setEnabled:!YES]; + clockTimer = [[NSTimer scheduledTimerWithTimeInterval:1 + target:self + selector:@selector(updateCurrentDate) + userInfo:nil + repeats:YES] retain]; } - (IBAction)startSpeechSampleText:(id)sender { @@ -56,6 +62,28 @@ //[voicesArrayController didChangeValueForKey:@"voice"]; // unnecessary } +#if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +- (NSDate *)currentDate +{ + return currentDate; +} +- (void)setCurrentDate:(NSDate *)aCurrentDate +{ + [aCurrentDate retain]; + [currentDate release]; + currentDate = aCurrentDate; +} +#else +@synthesize currentDate; +#endif + +- (void)updateCurrentDate +{ + //[self willChangeValueForKey:@"currentDate"]; // unnecessary + [self setCurrentDate:[[NSDate alloc] init]]; + //[self didChangeValueForKey:@"currentDate"]; // unnecessary +} + - (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)success {
----