☆Cocoa Touch UIKit勉強(5)
UIResponder
UIResponderはUIKitのルートとも言えるコントロールで、 その殆どのクラスがこれの子クラスである。 画面タッチやデバイスの傾き検知、シェイクなどのイベントを一括管理している。

UIResponderクラスは、イベントに反応して取り扱うオブジェクトのために、インターフェースを定義する。 これは、UIApplication、UIViewとその子クラス(UIWindowを含む)の親クラスである。 これらのクラスのインスタンスは、レスポンダー(オブジェクト)と呼ばれる。 イベントには大きく分けて「タッチイベント」と「モーションイベント」の2つがある。

touchesで始まる名称のメソッドがタッチイベント用である。 指が画面に触れる・ドラッグされる・画面から離れる時はUIEventオブジェクトが生成される。 指が画面上にあるまたは離れるときのイベントオブジェクトはUITouchオブジェクトを含む。 装置を振る運動;モーションイベントはmotionで始まる名前のメソッドである。

 UIMenuControllerを使ったメニューへの応答をする場合にはFirstResponderになる必要がある。 この辺りは画面回転と絡んで結構面倒なので要注意。

UIResponder
メソッド名動作
-(BOOL)becomeFirstResponderウインドウの第一レスポンダーになる
-(BOOL)resignFirstResponderウインドウの第一レスポンダーを放棄する
-(BOOL)canBecomeFirstResponder第一レスポンダーになれるかどうかを返す
-(BOOL)isFirstResponderレシーバーが第一レスポンダーかどうか
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)eventビューまたはウインドウ内にタッチダウンされた
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)eventビューまたはウインドウから指が離された
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)eventビューまたはウインドウ内で指の移動が検出された
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)eventタッチイベントがキャンセルされた
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)eventモーションイベントが開始された
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)eventモーションイベントが終了した
-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)eventモーションイベントがキャンセルされた

UITextField/UITextViewでキーボードを表示するには
    [textField becomeFirstResponder];
    [textView  becomeFirstResponder];
、消去するには
    [textField resignFirstResponder];
    [textView  resignFirstResponder];
と記述する。これは定型文なので、覚えておくしかない。

UIApplication
UIApplication・・・通常、このクラス自体を直接操作する必要はない。 Xcodeがひな形を書き出してるAppDelegate.m(だいたいこんな名前にする)がUIApplicationプロトコルに準拠するので、 そのメソッドを実装するだけである。
UIApplication

デリゲートは UIApplicationDelegate プロトコルで実装する。プログラム起動時を含め絶対に書かなければならない部分もある。
メソッド名動作
-(void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions アプリケーションが起動される時呼び出される。もちろん必須。
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation UIDocumentInteractionControllerでのファイル共有受信時に呼び出される。 URLで指定されるリソースを開くことができる。
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window アプリケーションでサポートする画面の回転方向を返す。
通常画面の回転方向は個別のUIViewControllerがsupportedInterfaceOrientationsで返すが、
それがないときに代わりにこれが呼び出される。画面ごとの特別な回転制御が必要ないなら、これだけで良い。
-(void)applicationDidFinishLaunching:(UIApplication *)application アプリケーションの起動が終わったとき呼び出される
-(void)applicationDidBecomeActive:(UIApplication *)application アプリケーションが再アクティブになったとき呼び出される。 非アクティブからの復帰時だけでなく、アプリケーション起動時にも呼ばれる。
-(void)applicationWillResignActive:(UIApplication *)application アプリケーションがアクティブになるとき=HOMEボタンを押したとき呼び出される。
-(void)applicationWillTerminate:(UIApplication *)application アプリケーションが終了するとき呼び出される。
HOMEボタンを押しただけでは非アクティブになるだけで、これは呼び出されない。
-(void)applicationDidEnterBackground:(UIApplication *)application アプリケーションがバックグラウンドに入ったとき呼び出される
-(void)applicationWillEnterForeground:(UIApplication *)application アプリケーションがフォアグラウンドに戻るとき呼び出される
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application アプリケーションがシステムからメモリ警告を受信した
-(void)application:(UIApplication *)application
willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration
didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
didReceiveLocalNotification:(UILocalNotification *)notification
didReceiveRemoteNotification:(NSDictionary *)userInfo

ステータスバーの方向が変わる前
 
ステータスバーの方向が変わったとき
Apple Push Serviceへ正常に登録できたとき
Apple Push Serviceが登録プロセスを正常に完了することができないとき
走っているアプリケーションがローカル通知を受信したとき
走っているアプリケーションがリモート通知を受信したとき

プロパティ名属性内容
id <UIApplicationDelegate> delegateSデリゲートを設定する
BOOL applicationSupportsShakeToEdit シェイクによるundo/redo操作を可能にする(デフォルトはYES)
NSInteger applicationIconBadgeNumber スプリングボード内のアプリケーションアイコンのバッジの現在番号(0=バッジを隠す;デフォルト)
UIApplicationState applicationStateRアプリケーションの実行状態
UIApplicationState(enum値)
UIApplicationStateActive フォアグラウンドで実行中&イベント受信状態
UIApplicationStateInactive フォアグラウンドで実行中だがイベントは受信していない
UIApplicationStateBackground バックグラウンド実行中
NSTimeInterval backgroundTimeRemainingRアプリケーションがバックグラウンドで動く時間
BOOL networkActivityIndicatorVisibleG=isNetworkActivityIndicatorVisibleネットワークが有効かどうかのインジケーターをON/OFFする
BOOL statusBarHiddenG=isStatusBarHiddenステータスバーを隠すかどうか
UIInterfaceOrientation statusBarOrientation アプリケーションステータスバー現在の方向
UIStatusBarStyle statusBarStyle 現在のステータスバーの形状
UIStatusBarStyle(enum値)
UIStatusBarStyleDefault 灰色(デフォルト)
UIStatusBarStyleBlackTranslucent半透明黒
UIStatusBarStyleBlackOpaque 不透明黒

メソッド名動作
-(UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void(^)(void))handler 新しい長く走るバックグラウンドタスクの開始を記録する
UIBackgroundTaskIdentifierはNSUInteger値でバックグラウンドで走るリクエストへのユニークなID
-(void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier 長く走るバックグラウンドタスクの終わりを記録する
-(void)beginIgnoringInteractionEventsタッチ関連のイベントの取扱いを停止するようにレシーバーに言う
-(void)beginReceivingRemoteControlEventsリモートコントロールイベントの受信を始めるようアプリケーションに通知する
-(void)endIgnoringInteractionEventsタッチ関連のイベントの取扱いを再開するようにレシーバーに言う
-(void)endReceivingRemoteControlEvents遠隔操作のイベントを受けるのを止めるようにアプリケーションに言う
-(void)presentLocalNotificationNow:(UILocalNotification *)notificationすぐにローカル通知を発行する
-(void)scheduleLocalNotification:(UILocalNotification *)notification内包された日時に発行するようローカル通知を予定する
-(void)cancelAllLocalNotificationsすべての予定のローカル通知の通知をキャンセルする
-(void)cancelLocalNotification:(UILocalNotification *)notification ローカル通知の通知をキャンセルする
-(BOOL)canOpenURL:(NSURL *)urlアプリケーションが与えられたURLリソースを開けるかどうか
-(void)clearKeepAliveTimeout前にインストールされた周期起動ハンドラーブロックを除去する
-(UIRemoteNotificationType)enabledRemoteNotificationTypesアプリケーションが受け入れる通知のタイプを返す
UIRemoteNotificationType(enum値)
UIRemoteNotificationTypeNone 通知を受け付けない
UIRemoteNotificationTypeBadge アプリケーションアイコンのバッジ
UIRemoteNotificationTypeSound 警告音通知
UIRemoteNotificationTypeAlert 警告メッセージ
-(BOOL)isIgnoringInteractionEventsレシーバーが画面タッチによって始められるイベントを無視しているかどうか
-(void)registerForRemoteNotificationTypes:(UIRemoteNotificationType)types Apple Push Serviceを通してプロバイダーから指定されたタイプ通知書を受け取るために登録する
-(void)unregisterForRemoteNotificationsApple Push Serviceからの通知を登録解除する
-(BOOL)sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent*)eventtargetにactionメッセージを送る
-(void)sendEvent:(UIEvent *)eventアプリケーション内の適切なレスポンダーにイベントを送る
-(void)setStatusBarHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation ステータスバーを表示・非表示する
UIStatusBarAnimation(enum値)
UIStatusBarAnimationNone アニメーションなし
UIStatusBarAnimationFade フェードイン・アウト
UIStatusBarAnimationSlide スライドイン・アウト
-(void)setStatusBarOrientation:(UIInterfaceOrientation)interfaceOrientation animated:(BOOL)animated ステータスバーの方向を設定する
-(void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle animated:(BOOL)animated ステータスバーの形状を設定する

ということで、iOS6でのAppDelegate.mの基本構造は以下のようになる(私が使っている定型)。
#import "AppDelegate.h"

// iOS6のiPhoneで倒立のPortraitがうまく回転しないのを回避するための処理
@implementation NavigationControllerForiOS6

- (NSUInteger)supportedInterfaceOrientations
{
    return supportedInterfaceOrientationsByMain();
}

- (BOOL)shouldAutorotate
{
    return shouldAutorotateByMain();
}
@end

@implementation AppDelegate

@synthesize window;
@synthesize navigationController;
@synthesize rootViewController;
@synthesize setup;

-(void)releaseResouces
// バックグラウンドに入るときに開放して良いリソースの開放
{
    NSLog(@"releaseResouces");
    ~
    [setup release];
    [navigationController release];
}   

-(void)reloadResouces
// リソースを読み直す
// バッググラウンドからの復帰時にも読みなおす
// ここに来たときにはmainViewとその中に含まれるrootViewは読み込み済みである
{
    NSLog(@"reloadResouces");
    
    ~
    
    // setupの読み出し;表示への反映は後で(ここで行っても反映されない)
    // 以下の初期化処理の中で参照されるのでここで先に読み込む
    setup=[[Setup alloc]init];
    
    // NavigationBarへルートとしての設定
    navigationController=[[NavigationControllerForiOS6 alloc]initWithRootViewController:rootViewController];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
// ファイル受け渡し起動時は、この後にopenURLが呼び出される
{
    // リソースの読み込み
    [self reloadResouces];
    
    // 主画面表示
    NSLog(@"主画面表示");
    [window setRootViewController:navigationController];
    [window makeKeyAndVisible];
    
    return YES; // 画面が表示されるのはこの後
}

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
// UIDocumentInteractionControllerでのファイル共有受信用
// すでに起動中の時はこれだけ呼び出される
{
    return [rootViewController importBasicFile:url];
}

//-------------------------------------------

-(void)applicationEnd
// アプリ終了処理
{
    NSLog(@"applicationEnd");
    // 各種停止処理
    [setup saveSetup]; // setupの保存と言うよりスクロール位置の保存
}

- (void)applicationWillTerminate:(UIApplication *)application
// アプリケーションが本当に終了するとき
// iOS4では通常はHOMEボタンを押してもここには来ない
// UIApplicationExitsOnSuspend=YESの時に来る(サスペンド不可設定時)
{
    NSLog(@"プログラム終了");
    [self applicationEnd];
}

- (void)applicationWillResignActive:(UIApplication *)application
// アプリケーションが非動作になるとき
// iOS4ではHOMEボタンを押すとここに来る
// UIApplicationExitsOnSuspend=NOの時に来る(サスペンド可設定時)
{
    NSLog(@"プログラム非動作");
    fnonActive=YES; // nonActiveになる
    [self applicationEnd];
    NSLog(@"プログラム非動作化終了");
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSLog(@"プログラム再アクティブ");
    
    if (!fnonActive) {
        // 起動時に、applicationWillResignActiveが呼ばれてないのに此処に来ることがあるので回避
        NSLog(@"→回避");
        return;
    }
    // マルチタスクで外部で設定が変更されているかもしれないので、読み込んでおく
    EnterRunLoop(0.0); // これがないと、「何故か」直前の「設定」での変更が反映されない
    ~ // 外部SETUP読み込み
}

//-------------------------------------------------------------------

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window_
// iOS6
// 画面回転を許可するために呼び出される・・・ようである
// 個別のUIViewControllerがsupportedInterfaceOrientationsを持ってないときはこれを呼び出すらしい
{
//  NSLog(@"application.supportedInterfaceOrientationsForWindow:window=%@",window_);
    return ~;
}

//-------------------------------------------------------------------

- (void)applicationDidEnterBackground:(UIApplication *)application
// アプリケーションがバックグラウンドに入った
{
    NSLog(@"applicationDidEnterBackground");
}

- (void)applicationWillEnterForeground:(UIApplication *)application
// アプリケーションはフォアグラウンドに入る
{
    NSLog(@"applicationWillEnterForeground");
}

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    NSLog(@"メモリ不足:applicationDidReceiveMemoryWarning");
}

- (void)dealloc
{
    [self releaseResouces];
    [super dealloc];
}

@end

UILocalNotification
UILocalNotificationはiOS4.0で追加された機能で、アプリケーションに通知する機能を強化する(特に周期起動に関する機能)。

UILocalNotificationのインスタンスは、アプリケーションが特定の日付と時間をユーザーに知らせる予定をすることができる通知機能を提供する。 オペレーティングシステムは、通知を適当な時刻に届ける。その時アプリケーションは動作している必要はない。
ローカル通知は、主にタイマーによる単純なカレンダーやTo-Doリストアプリケーションを目的とする。

UILocalNotification
プロパティ名属性内容
NSDate *fireDate Cシステムが通知を発送する日時
NSString *alertAction Cアクションボタンまたはスライダーのタイトル文字列
NSString *alertBody C警告通知で表示されるメッセージ
NSString *alertLaunchImage Cユーザーがアクションボタンを軽くたたくまたはスライダーをスライドするとき、
起動イメージとして使われるイ画像のファイル名を返す
NSInteger applicationIconBadgeNumber アプリケーションアイコンのバッジの表示する番号(デフォルト=0;変更なし)
BOOL hasAction  通知がアラートアクションを表示するか否か
NSCalendar *repeatCalendarC繰り返し通知するときにシステムが参照するカレンダー
NSCalendarUnit repeatInterval 繰り返し通知の周期
NSString *soundName C表示されるときに再生される音声ファイル名
NSTimeZone *timeZone CfireDateのタイムゾーン
NSDictionary *userInfo C通知されたアプリケーション付加するユーザー情報


UIDocumentInteractionController
UIDocumentInteractionControllerは、アプリケーション間でファイルの受け渡しをするために使うクラス、らしい。 詳しくはここに紹介があるので読んでもらうとして、 ここでは、独自の拡張子のファイルを受け取る方法のみ説明する。

渡されるアプリケーション側で用意するのは、
  1. DocumentTypeとExported UTIs
  2. 受信処理
の2つだけである。受信処理はUIApplicationのapplication: openURL:~だけであり、 適当な受信処理(たとえばファイル読み込み)をそこに書くだけで良い。

実際にアプリケーションがどのファイルを受信出来るかと言うことをOSに知らせるのが「DocumentType」「Exported UTIs」の設定である。 ここに適切な設定をしておくと、「ファイルの出力をサポートしたアプリケーション」、一番わかりやすいのは 「メール」でファイルのアイコンをタップすると、出力先としてこのアプリケーションが表示されるようになる。

「DocumentType」は、定義済みのファイルを割り付ける部分である。
Name 内容 単なる説明文
Types 識別子 対応するファイル形式
Icon アイコン ここ参照
Typesで指定するファイル形式については ここに詳細があるがちとわかりにくい。 表の「Identifer」または「Confirms To」を','で区切って羅列する。 どちらでも良いが、後者の方がより幅広いファイルに対応してくれる。 拡張子の大文字小文字は区別されない(両方に対応する)。
なお、「Types」にはpublic.jpegおよびpublic.pngは指定出来ない(他の画像系ファイルもだめかも)。というか、指定しても無視される。 少なくともメールでは設定が無視された。 iOS側で自動設定しているのかもしれない。public.audioは問題ない。

独自の拡張子を持つファイルをサポートさせる場合は、まず「DocumentType」にTypesとして独自の形式名を設定する。

さらに、「Exported UTIs」に、それによって受信するファイルの情報を設定する。 受信であるにもかかわらず「Exported」という名前なのでややこしいが、OSに対して知らせておく=出力しておくという意味なのだろう。

Description 内容 単なる説明文
Identifer 識別子 DocumentTypeで指定したTypesを記入する
Confirms To ファイル形式
SmallIcon 小アイコンここ参照
LargeIcon 大アイコンここ参照
Additional exported UTI properties 追加属性情報 独自の拡張子を持ったファイルをサポートするときは、ここの設定が必要になる。 拡張子情報辞書
UTTypeReferenceURL そのファイルについて説明しているURLアプリのサポートURLを書いとけば良いと思う
UTTypeTagSpecification
public.filename-extension拡張子名群
「Confirms To」には、設定するファイルを含む形式を指定する。指定仕方はTypesと同じ。 拡張子群には、対応させる拡張子を記述する。ここでも大文字小文字は区別されない。

アイコンについては、サイズの他、自動的に掛けられる効果の考慮も必要なので、 基本的には指定せずにOSに任せた方が良さそうである。 他のアイコン画像から自動的に作ってくれるようである。

これで外部アプリケーションからファイルの受信が可能になる。 この方法では同時に1つしかファイルが渡せないが、それについては他の共有方法を使うか、 X-BASIC for iOSの場合はZipファイルを使って渡すことで解決している。

なお、アップルではスクリプトも含め実行形式のファイルのインポートを一切認めない(その機能があると審査で拒絶される)。 ZIPの中に入れてもだめである。 実装時はデータファイルのみインポートするようにしなければならない。