トランジションアニメーション(1)

2010. 06. 26
次にトランジションアニメーションの導入をします。

トランジションアニメーションは、変更前と後の画像の遷移を表すアニメーションで、左右のフリップと上下のカールの4種類あります。

451

左フリップ(UIViewAnimationTransitionFlipFromLeft)

452

上カール(UIViewAnimationTransitionCurlUp)

本書では、カールアニメーションがiPhone Simulatorではフェードエフェクトで代替表現されるとありますが、現在のiPhone Simulator(バージョン4.0(211.1)。Xcode 3.2.3 and iPhone SDK 4(10M2262)に入っているもの)ではカールアニメーションも表示されます。


・UIViewAnimationTransition
(UIViewクラス)

typedef enum {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
} UIViewAnimationTransition;

アニメーションブロック内のビューに、指定した遷移を適用します。

UIViewAnimationTransitionNone
遷移を指定しません。

UIViewAnimationTransitionFlipFromLeft
(画像の中心を通る)縦軸を中心に左から右にビューが回転する、フリップ(裏返す)の遷移を行います。
ビューの左側が手前に、右側が奥に向かって動きます。

UIViewAnimationTransitionFlipFromRight
縦軸を中心に右から左にビューが回転する、フリップの遷移を行います。
ビューの右側が手前に、左側が奥に向かって動きます。

UIViewAnimationTransitionCurlUp
ビューの下部から上へ巻き上げる遷移を行います。

UIViewAnimationTransitionCurlDown
ビューの上部から下へ巻き下げる遷移を行います。


●トランジションアニメーションの実装

トランジションアニメーションを実装するには、遷移に必要な前後2枚の画像をアニメーションブロック内で読み込む必要があります。

そのため、遷移のトリガとしてスライドショー用のタイマー(timerSlide)を追加し、startStop:メソッドに組み込みます。

タイマーが発火するとトランジションアニメーションを実行するnextSlide:メソッドを呼び出します。

nextSlide:メソッド内で、画像配列imagesから次に表示する画像番号curImageIndexを取得し、画像の遷移を行います。

また画像の切り替え速度の変化に対応するため、changeDuration:メソッドもタイマーに置換します。


●Slide.hの編集

スライドショー用のタイマーと(timerSlide)、画像配列(images)、そして遷移用に次に表示する番号を配列から取得するための現在表示している配列番号(curImageIndex)を宣言します。
(太字が追加した部分)

#import <Foundation/Foundation.h>

@interface Slide : NSObject {

    // アウトレット
    IBOutlet UIImageView *imageSlide;    // 画像表示
    IBOutlet UIToolbar *toolbar;    // ツールバー
    IBOutlet UISlider *sliderDuration;    // 長さ変更
    IBOutlet UISegmentedControl *segShuffle;    //シャッフル
    IBOutlet UIButton *btnStartStop;    // 開始ボタン

    NSTimer *timerSlide;    // スライド用タイマー
    NSArray *images;    // スライド用画像配列
    int curImageIndex;    // 現在表示している画像の番号
}

// アクション
- (IBAction)startStop:(id)sender;    // スライドショーの開始停止
- (IBAction)changeOrder:(id)sender;    // スライドショーのシャッフル
- (IBAction)changeDuration:(id)sender;    // スライドショーの長さの変更

- (void)enterOrExitFullScreen;
- (void)enterOrExitFullScreenWithAnimation;

@end

453


●タイマー用画像配列と表示画像番号の初期化

タイマー用画像配列と表示画像番号の初期化をawakeFromNibメソッドに追加するとともに、不要になるUIImageViewのアウトレット(imageSlide)の処理を削除します。
(太字が追加した部分)

- (void)awakeFromNib {
    // スーパークラスによる初期化
    [super awakeFromNib];

    // 画像配列を自前の配列に保持
    images = [[self animationImages] retain];

    //表示画像番号を初期化
    curImageIndex = 0;

    // タイマーは動かさない
    timerSlide = nil;

    // UIButtonに画像を貼り付ける

    // 背景画像の読み込み
    UIImage *imgBtn1 = [UIImage imageNamed:@"grayButton.png"];

    // 背景画像から伸縮画像を生成
    UIImage *imgBtn2 = [imgBtn1 stretchableImageWithLeftCapWidth:13 topCapHeight:0];

    // 伸縮可能画像をUIButtonの背景に設定
    [btnStartStop setBackgroundImage:imgBtn2 forState:UIControlStateNormal];
}

454

本書内のコードでは、画像配列の読み込みが『setupImageArrayFromResource』(XML化時に名前を変更したメソッド)になっています。
(詳細は『スライドショーの画像配列をXML化』を参照してください)

このメソッド名変更は本書内では説明されておらず、サンプルコードで実装されているだけなので気付き難いかもしれません。

ここではanimationImagesに修正しています。


●Start/Stopボタンのアクションメソッドのタイマー化

Start/Stopボタンによるアニメーションの再生/停止を、UIImageViewによるアニメーションからタイマーによる発火に置き換えます。

また、スライドショー中は無操作でも自動スリープに入らない機能を追加しています。
(太字が追加・修正した部分)

- (IBAction)startStop:(id)sender {
    // アニメーション中かどうかの判定
    BOOL isAnimating = (timerSlide != nil);

    // アニメーション停止処理
    if (isAnimating == YES) {
        // タイマーを止め、nilに戻す
        [timerSlide invalidate];
        timerSlide = nil;
        [btnStartStop setTitle:@"Start" forState:UIControlStateNormal];
    }
    else {
        // タイマーを生成
        timerSlide = [NSTimer scheduledTimerWithTimeInterval:sliderDuration.value / [images count] target:self selector:@selector(nextSlide:) userInfo:nil repeats:YES];
        [btnStartStop setTitle:@"Stop" forState:UIControlStateNormal];
    }

    // スリープを抑制
    UIApplication *app = [UIApplication sharedApplication];
    app.idleTimerDisabled = isAnimating;
}

455

アニメーション中かどうかの判定を、UIImageViewのisAnimatingメソッドからスライドショー用timerSlideの有無にして、タイマーが有ればアニメーション中、無ければ停止中と判別します。

したがって判定後の処理も、アニメーションを停止する場合はタイマーの停止と無効化、再生する場合はタイマーの生成を行っています。

タイマーが発火するとトランジションアニメーションを実行するnextSlide:メソッドを呼び出します。


・invalidate
(NSTimerクラス)

- (void)invalidate

レシーバのタイマーを要求があるまで、再発火の停止および実行ループからの削除をします。

このメソッドはNSRunLoopオブジェクトからタイマーを除去する唯一の方法です。

NSRunLoopオブジェクトは、invalidメソッドが返す直前か少し後の時点でタイマーの除去と解放を行います。

ターゲットとユーザ情報オブジェクトと共に構成されている場合、レシーバは参照しているこれらのオブジェクトも解放します。

特別な考慮事項

このメッセージは、タイマーを設置しているスレッドからメッセージを送る必要があります。

このメッセージを他のスレッドから送信した場合、入力ソースに関連付けられているタイマーが実行ループから除去されず、スレッドが適切に終了できなくなります。


・idleTimerDisabled
(UIApplicationクラス)

@property(nonatomic, getter=isIdleTimerDisabled) BOOL idleTimerDisabled

アプリケーションのアイドルタイマーを無効にするかどうかを制御するブール値です。

このプロパティのデフォルト値はNOです。

アプリケーションでタッチ操作による短期間ユーザの入力が無い場合、システムはスリープ状態になり、デバイスの画面が薄暗くなります。

これは省電力の目的のために行われます。

しかし、ゲームなどの加速度センサ以外のユーザ入力の無いアプリケーションの場合、このプロパティをYESにすることでアイドルタイマーを無効にして、システムのスリープを防ぐことができます。

重要:このプロパティは必要な場合にのみ設定し、必要が無くなったらNOにしてリセットする必要があります。

アプリケーションは、アイドルタイマーが経過した時にシステムがスクリーンをオフにできるようにする必要があります。

これにはオーディオアプリケーションも含まれます。

適したオーディオセッションサービスを使い、スクリーンがオフになっても継続して再生や録音ができるようにしてください。

地図アプリケーションやゲームなどの、散発的にユーザと対話するアプリケーションでは、アイドルタイマーを無効にする必要があります。


●トランジションアニメーションを行うメソッドの実装

タイマー発火時に呼び出される、トランジションアニメーションを実行するnextSlide:メソッドを実装します。

- (void)nextSlide:(NSTimer *)timer {

    // 表示画像番号の更新
    curImageIndex++;
    if ([images count] <= curImageIndex) {
        curImageIndex = 0;
    }

    // アニメーションの準備
    [UIView setAnimationsEnabled:YES];
    [UIView beginAnimations:@"Slide" context:nil];
    [UIView setAnimationDuration:1.0f];

    // トランジションアニメーションの設定
    [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:imageSlide cache:YES];

    // 画像の切り替え
    imageSlide.image = [images objectAtIndex:curImageIndex];

    // アニメーション実行
    [UIView commitAnimations];
}

456

最初に現在表示されている画像配列の番号(curImageIndex)をインクリメント(+1加算)して、トランジションアニメーションに用いる次の画像番号を得ます。

画像番号が画像配列の要素数を超える場合には0に戻しています。

setAnimationsEnabled:でアニメーションを有効に、beginAnimations:でアニメーションブロックを開始、setAnimationDuration:でアニメーションする時間を設定します。

トランジションアニメーションは、setAnimationTransition:forView:cache:メソッドで行います。

今回は4種類の中からカールアップを選択し、キャッシュも有効にしています。

そしてobjectAtIndex:メソッドでトランジション後の画像オブジェクトをimageプロパティで入れ替え、commitAnimationsでアニメーションブロックを終了します。


・setAnimationDuration:
(UIViewクラス)

+ (void)setAnimationDuration:(NSTimeInterval)duration

アニメーションブロックで、アニメーションを行うプロパティの継続時間を秒単位で設定して変更します。

このメソッドは、アニメーションブロックの外側で呼び出された場合は何もしません。

アニメーションブロックはbeginAnimations:context:クラスメソッドで開始し、commitAnimationsクラスメソッドで終了します。

デフォルト値は0.2(秒)です。

このメソッドは、iPhone OS 4.0以降は推奨されません。

代わりにブロックベースのアニメーションメソッドを使用してください。

duration:アニメーションを継続する期間を指定します。


・setAnimationTransition:forView:cache:
(UIViewクラス)

+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache

アニメーションブロック中にビューの適用する遷移を設定します。

UIViewのインスタンスであるコンテナビューを使って、ビューの外観変更の遷移する(例えば、あるビューから別のビューにフリップで切り替わる)場合、以下のような手順になります。

1.アニメーションブロックの開始
2.コンテナビューの遷移を設定
3.コンテナビューからサブビューを削除
4.コンテナビューに新しいサブビューを追加
5.アニメーションブロックを確定

このメソッドは、iPhone OS 4.0以降は推奨されません。

代わりにブロックベースのアニメーションメソッドを使用してください。

transition:viewに適用する遷移を指定します。
指定可能な値は、UIViewAnimationTransitionに表されています。

view:遷移を適用するビューを指定します。

cache:YESの場合、viewの前後の画像を一度描画し、アニメーションのフレーム生成に使用します。
このパラメータをYESに設定すると、キャッシュにより性能を向上させることができますが、遷移中にビューまたはサブビューを更新することはできません。
アニメーション中にビューとサブビューを更新すると、キャッシュの動作を妨げるだけでなく、間違った内容や場所の表示になります。
遷移が終了するまでビューの更新を待つ必要があります。
NOの場合、遷移アニメーションの各フレームごとに、ビューとその内容を更新する必要がありますが、フレームレートに著しく影響を与える可能性があります。


・image
(UIImageViewクラス)

@property(nonatomic, retain) UIImage *image

イメージビューに画像を表示します。

このプロパティの初期値は、initWithImages:メソッドの画像が渡されるか、レシーバが初期化に別のメソッドを渡す場合はnilになります。

animationImagesプロパティの値にnil以外が含まれている場合は、このプロパティの内容は使用されません。

imageプロパティでは、UIImageViewのサイズを変更する設定はありません。

画像をビューのサイズに適応させる場合は、sizeToFitを呼び出してください。


●速度切り替えバーのアクションメソッドのタイマー化

スライダによるアニメーション速度の切り替えを行うchangeDuration:メソッドも、タイマーに置き換えます。

タイマーの間隔は後から変更できないので、一旦タイマーを停止・無効化して、生成し直します。
(太字が追加・修正した部分)

- (IBAction)changeDuration:(id)sender {

    // タイマーを止める
    [timerSlide invalidate];

    // タイマーを生成
    timerSlide = [NSTimer scheduledTimerWithTimeInterval:sliderDuration.value / [images count] target:self selector:@selector(nextSlide:) userInfo:nil repeats:YES];
}

457

invalidateメソッドで、タイマーを一旦停止・無効化します。

そしてscheduledTimerWithTimeInterval:target:selector:userInfo:repeats:メソッドでタイマーを作り直しています。

発動間隔はスライダの値(sliderDuration.value)を画像配列の要素数で割ったものを使用しています。



参考文献

UIView Class Reference

NSTimer Class Reference

UIApplication Class Reference

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

スライドショーの簡易アニメーション(3)

2010. 06. 24
●ビューの回転機能

続いてビューの回転機能を付加します。

といってもデバイスの向きを検出してインターフェイスごと回転させるのではなく、トリプルタップによる画像表示ビューのみを右方向に90度回転させる(回転済みの場合は元に戻す)メソッドrotate90を追加するものです。


●トリプルタップ処理の追加

スライドショーのフルスクリーン化(2)』の『UITouchableImageView.mソースファイルの編集』での説明と同様の手順で、UITouchableImageView.mに

・トリプルタップ処理を呼び出すメソッドtripleTapの追加
・タップ回数を判別するtouchesEnabled:withEvent:メソッドへのトリプルタップ処理の追加

を行います。

サンプルコード『iPhoneBG2/SampleCode/60.04.Slide ( アニメ )』のtouchesEnabled:withEvent:メソッドのcase 3のコメントが『// 2 ダブルタップの処理』となっていますが、正しくは『// 3 トリプルタップの処理』です。
(太字が追加した部分)

- (void)singleTap {
    if (tapCount == 1) {    // シングルタップの確認
        // フルスクリーンの開始と解除
        [slide enterOrExitFullScreen];
    }
}

- (void)doubleTap {
    if (tapCount == 2) {    // ダブルタップの確認
        // スライドショーの開始と停止
        [slide startStop:nil];
    }
}

- (void)tripleTap {
    if (tapCount == 3) {    // トリプルタップの確認
        // 回転
        [self rotate90];
    }
}


- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    // タッチ情報の集合から1つを取り出す
    UITouch *touch = [touches anyObject];

    // タップカウントを取得
    tapCount = touch.tapCount;

    switch (tapCount) {
        case 1:    // シングルタップの処理
            [self performSelector:@selector(singleTap) withObject:nil afterDelay:0.4f];
            break;
        case 2:    // ダブルタップの処理
            [self performSelector:@selector(doubleTap) withObject:nil afterDelay:0.4f];
            break;
        case 3: // トリプルタップの処理
            [self performSelector:@selector(tripleTap) withObject:nil afterDelay:0.4f];
            break;
    }
}

446

tripleTapメソッドは、タップカウントが3回の場合にビューの回転を行うrotate90メソッド(後述)を呼び出します。

tripleTapメソッドはUITouchableImageViewクラス内で記述するので、レシーバはslideではなくselfになることに注意してください。

touchesEnabled:withEvent:メソッドのcase 3は、タップカウントが3回の場合にセレクタで上記tripleTapメソッドを呼び出します。


●アフィン変換によるビューの90度回転

- (void)rotate90 {
    // アニメーションの準備
    [UIView setAnimationsEnabled:YES];
    [UIView beginAnimations:@"Rotate" context:nil];

    // 既に回転済みかを確認
    if (CGAffineTransformIsIdentity(self.transform)) {
        CGAffineTransform trans;

        // 90度回転するアフィン変換を生成
        trans = CGAffineTransformMakeRotation(3.14 / 2.0);

        // 既存のアフィン変換を拡大縮小(480dot→320dot)
        trans = CGAffineTransformScale(trans, 320.0 / 480.0, 320.0 /480.0);

        // アフィン変換を変更
        self.transform = trans;
    }
    else {
        self.transform = CGAffineTransformIdentity;
    }
    // アニメーション実行
    [UIView commitAnimations];
}

447

まずsetAnimationsEnabled:メソッドでアニメーションを有効にします。

次にbeginAnimations:context:メソッドで、アニメーションブロックを開始します。

続いてアフィン変換を使って、時計回りに90度回転と横幅に合わせた縮小のON/OFFを行います。

自身のアフィン変換(UITouchableImageViewのアフィン変換であるself.transform)がオリジナルのアフィン変換(無変換)と同じかどうかを判別し、オリジナルなら回転と縮小の変換をし、変換済みの場合はオリジナルに戻します。

オリジナルのアフィン変換かどうかの判別は、CGAffineTransformIsIdentity関数で行います。

回転はCGAffineTransformMakeRotation関数を用い、角度をラジアンで指定します。

ラジアンは 1π = 180度ですから、90度回転する場合『(3.14 / 2.0)』で間違いは無いのですが、他書籍などでは見た目に分かり易くするため『(90.0 * (M_PI / 180.0))』と記述することが多いようです。

一つ引っかかるのは、『CGAffine Transform Reference』のCGAffineTransformMakeRotationの説明で、引数angleの項に

『In iPhone OS, a positive value specifies counterclockwise rotation and a negative value specifies clockwise rotation. 』

iPhone OSでは正の値を指定すると反時計回りに回転し、負の値を指定すると時計回りに回転します。

とあり、実際の動作と逆になっているのが腑に落ちないところです。

それはさておき、縮小はCGAffineTransformScaleで行っているのですが、本書では値が整数で書かれていますがこのままでは挙動がおかしくなります。

(サンプルコードは正しく記述されていますが)CGAffineTransformScaleの拡大縮小の引数は実数(CGFloat型)で渡す必要がありますので注意してください。

回転・縮小済みのアフィン変換だった場合は、CGAffineTransformIdentityでオリジナルのアフィン変換に戻します。

最期にcommitAnimationsメソッドを呼び出して、アニメーションブロックを閉じます。


・CGAffineTransformISIdentity
(CGAffineTransform)

bool CGAffineTransformIsIdentity (
    CGAffineTransform t
);

アフィン変換が適用されていないものかをチェックします。

tが適用されていない場合はtrue、それ以外はfalseを返します。

t:チェックするアフィン変換を指定します。


・transform
(UIViewクラス)

@property(nonatomic) CGAffineTransform transform

区域の中心から相対的に、指定した変換をレシーバに適用します。

変換の原点の値はcenterプロパティか、変更されている場合はレイヤのanchorPointプロパティで得られます。
(取得には、元となるCore Animationレイヤオブジェクトのlayerプロパティを使用します)

デフォルト値はCGAffineTransformIdentityです。

このプロパティを変更して、アニメーションをすることができます。

アニメーションブロックで、開始にはbeginAnimations:context:クラスメソッド、終了にはcommitAnimationsクラスメソッドを使います。

デフォルトでは全てcenter値(変更されている場合はanchorPoint値)です。

警告:このプロパティが恒等変換できなかった場合は、frameプロパティの値が未定義となって無視してください。


・CGAffineTransformMakeRotation
(CGAffineTransform)

CGAffineTransform CGAffineTransformMakeRotation (
    CGFloat angle
);

提供した値で回転させたアフィン変換行列を返します。

この関数はCGAffineTransform構造体を生成し、座標系の回転に使用(必要なら再使用)することができます。

行列は以下のような形式になっています。

448

実際の回転の方向は、ターゲットとなるプラットフォームの座標系に依存し、iPhone OSとMac OS Xでは異なります。

この関数で返されるCGAffineTransformデータ構造体は、最初の二つの列のみに値が含まれるため、三列目は常に (0, 0, 1) です。

これらの方程式の結果は、Quartzが点 (x, y) の回転を適用するのに使用します。

449

回転させたオブジェクトを描画にのみ使う場合は、アフィン変換を構成する必要はありません。

回転させた描画を行う最も直接的な方法は、CGContextRotateCTM関数を呼び出すことです。

angle:この行列で回転させる座標系軸の角度をラジアンで指定します。
iPhone OSでは正の値を指定すると反時計回りに回転し、負の値を指定すると時計回りに回転します。
Mac OS Xでは正の値を指定すると時計回りに回転し、負の値を指定すると反時計回りに回転します。


・CGAffineTransformScale
(CGAffineTransform)

CGAffineTransform CGAffineTransformScale (
    CGAffineTransform t,
    CGFloat sx,
    CGFloat sy
);

現在のアフィン変換をスケーリングしたアフィン変換行列を返します。

この関数は、現在のアフィン変換の値にスケーリングした値を追加した、新しいアフィン変換行列の生成に使用します。

新しいアフィン変換は、座標系の拡大縮小に使用(必要なら再使用)することができます。

t:現在のアフィン変換を指定します。

sx:拡大縮小するアフィン変換のx値を指定します。

sy:拡大縮小するアフィン変換のy値を指定します。


・CGAffineTransformIdentity
(CGAffineTransform)

const CGAffineTransform CGAffineTransformIdentity;

恒等変換。
※ 無変換のアフィン変換

450


●CGAffineTransform

CGAffineTransformデータ構造体は、アフィン変換を使った行列を表します。

アフィン変換は、ある座標系の点を指定して、別の座標系に点をマッピングすることです。

アフィン変換は特別なマッピング方法で、長さや角度を維持した平行移動のことですが、必ずしもそうとは限りません。

アフィン変換は平行移動だけでなく、スケーリング(拡大/縮小)、回転といった操作をサポートしていて一般的によく使われますが、斜めに(平行四辺形のように、相対する二辺が平行を維持した変形)することもできます。

QuartzはCGAffineTransformデータ構造体を使って、アフィン変換の生成、連結、適用をする関数を提供します。

アフィン変換関数の使用法についての情報は『Quartz 2D Programming Guide』を参照してください。

通常はアフィン変換を直接生成する必要はありません。

現在のアフィン変換を修正する関数はCGContext Referenceに表されています。

アフィン変換を再利用する予定が無い場合は、CGContextScaleCTM、CGContextRotateCTM、CGContextTranslateCTM、またはCGContextConcatCTMを使用することができます。



参考文献

CGAffine Transform Reference

UIView Class Reference

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

スライドショーの簡易アニメーション(2)

2010. 06. 23
スライドショーの簡易アニメーション(1)』で、『Slide.mソースファイルの編集』でのenterOrExitFullScreenWithAnimationメソッドの解説を忘れていましたので、その説明からします。

●Slide.mソースファイルの編集(の続き)

このメソッドでは、シングルタップによるフルスクリーン化時に、ステータスバーの透明化と(下方向へ画面外への)ツールバーの移動を行います。

アニメーションブロック内で設定するのはツールバーのみで、ステータスバーについてはアニメーションブロック外でステータスバー専用メソッドでアニメーションの設定を行います。

まずsetAnimationsEnabled:メソッドでアニメーションを有効にします。

次にbeginAnimations:context:メソッドで、アニメーションブロックを開始します。

AnimationIDは『FullScreen』とし、追加情報は無いのでcontextはnilとします。

ツールバーの移動は、UIToolbarクラスの親クラスであるUIViewクラスのプロパティを主に使っています。

centerプロパティでツールバーの中心位置、frameプロパティで矩形、CGRectGetHeightで高さを取得します。

ON/OFFの切り替えはツールバーのアルファ値(alphaプロパティ)で判定しています。

アルファ値が1.0fの場合はalphaプロパティを0.0fに、center.y(ツールバーの中心y座標)をheight(ツールバーの高さ)分加算して下方向に移動させて隠します。

アルファ値が0.0fの場合は、その逆になります。

そしてcommitAnimationsメソッドを呼び出して、アニメーションブロックを閉じます。

ステータスバーのアニメーションは、sharedApplicationメソッドでアプリケーションのインスタンスを呼び出し、setStatusBarHidden:animated:メソッドで表示/非表示とアニメーションの有無を設定しています。


・frame
(UIViewクラス)

@property(nonatomic) CGRect frame

レシーバのフレーム矩形です。

この矩形は、点で正確に測定されます。

スーパービューのシステム座標で、レシーバのフレーム矩形の再配置とリサイズの設定を行います。

フレームの原点は、スーパービューの座標です。

このプロパティの値は、centerとboundsプロパティの変更に応じて設定されます。

フレーム矩形を変更すると、drawRect:メソッドの呼び出し無しにレシーバを自動的に再表示します。

フレーム矩形の変更時にdrawRect:メソッドを呼び出したい場合は、UIViewContentModeRedrawにcontentModeプロパティを設定してください。

このプロパティを変更して、アニメーションをすることができます。

アニメーションブロックで、開始にはbeginAnimations:context:クラスメソッド、終了にはcommitAnimationsクラスメソッドを使います。

transformプロパティを設定する場合は、代わりにboundsとcenterプロパティを使用してください。

それ以外の場合、frameプロパティのアニメーションの変更が、実際のビューの位置に正確に反映されません。

警告:transformプロパティが恒等変換できなかった場合は、未定義になって無視してください。


●UITouchableImageView.mの編集

本書では触れられていませんが、シングルタップ時の処理を呼び出すメソッドを置き換えます。

UITouchableImageView.mのsingleTapメソッドで、enterOrExitFullScreenを呼び出している部分をenterOrExitFullScreenWithAnimationに修正します。
(太字が変更した部分)

- (void)singleTap {
    if (tapCount == 1) { // シングルタップの確認
        // フルスクリーンの開始と解除
        [slide enterOrExitFullScreenWithAnimation];
    }
}

444

ここを修正し忘れて、アニメーションにならないと10分程悩んでいたのは内緒です。


●ビルド時の警告

『ビルドと実行』を行うと『問題なく完了しました』と言いつつ、『'setStatusBarHidden:animated:' is deprecated』というWarningが出ます。

setStatusBarHidden:animated:はiPhone OS 3.2(つまりiPad)には非推奨で、setStatusBarHidden:withAnimation:メソッドを使うよう推奨しているためです。

ちなみにsetStatusBarHidden:animated:はiPhone OS 2.0以降で利用可(iPhone OS 3.2は不可)であるのに対し、setStatusBarHidden:withAnimation:はiPhone OS 3.2以降となっていますので、iOS4ではどちらも可ではありますが、警告が残っているのも気持ちが悪いので置き換えてみます。
(太字が置換した部分)

- (void)enterOrExitFullScreenWithAnimation {
    // ビューのアニメーションを有効化
    [UIView setAnimationsEnabled:YES];

    // アニメーション定義ブロックの開始
    [UIView beginAnimations:@"FullScreen" context:nil];

    // 現在のビューの中心位置を取得
    CGPoint center = toolbar.center;

    // 現在のビューの矩形と高さを取得
    CGRect frame = toolbar.frame;
    CGFloat height = CGRectGetHeight(frame);

    // 不透明の時は透明化して下に移動
    if (toolbar.alpha == 1.0f) {
        toolbar.alpha = 0.0f;
        center.y += height;
    }
    // 透明の時は不透明化して上に移動
    else {
        toolbar.alpha = 1.0f;
        center.y -= height;
    }

    // 中心位置を変更
    toolbar.center = center;

    // アニメーション実行
    [UIView commitAnimations];

    // ステータスバーのアニメーション
    UIApplication *app = [UIApplication sharedApplication];
    [app setStatusBarHidden:!app.statusBarHidden withAnimation:UIStatusBarAnimationFade];
}

445

これで警告も出なくなります。


・setStatusBarHidden:withAnimation:
(UIApplicationクラス)

- (void)setStatusBarHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation

ステータスバーの非表示または表示、任意で変化をアニメーションできます。

詳細はUIStatusBarAnimation型の定数を参照してください。

hidden:YESの場合はステータスバーを非表示に、NOの場合は表示になります。

animated:ステータスバーのアニメーション方法を示す定数で、非表示時にフェードまたはスライドの指定をします。


・UIStatusBarAnimation
(UIApplicationクラス)

 typedef enum {
    UIStatusBarAnimationNone,
    UIStatusBarAnimationFade,
    UIStatusBarAnimationSlide,
} UIStatusBarAnimation;

ステータスバーの表示/非表示時のアニメーションに適用されます。

この定数は、UIStatusBarAnimation型のsetStatusBarHidden:withAnimation:メソッドの引数です。

UIStatusBarAnimationNone

ステータスバーの表示/非表示にアニメーションを適用しません。

UIStatusBarAnimationFade

ステータスバーの表示/非表示に、フェードイン/フェードアウトを行います。

UIStatusBarAnimationSlide

ステータスバーの表示/非表示に、スライドイン/スライドアウトを行います。



参考文献

UIView Class Reference

UIApplication Class Reference

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

スライドショーの簡易アニメーション(1)

2010. 06. 21
今度はステータスバーの透明化とツールバーの移動、ならびにトリプルタップ時のビューの回転機能のアニメーションをアプリケーションに追加します。


●フルスクリーン化時のステータスバーとツールバーのアニメーション

前述の通り、シングルタップした際のフルスクリーン化のON/OFF時に、

・ステータスバーの透明化
・(下方向へ画面外への)ツールバーの移動

の2つのアニメーション機能を、SlideクラスにenterOrExitFullScreenWithAnimationメソッドとして追加します。


●Slide.hヘッダファイルの編集

本書では触れられていませんが、Slide.hヘッダファイルにenterOrExitFullScreenWithAnimationの宣言するのを忘れないでください。
(太字が追加した部分)

#import <Foundation/Foundation.h>

@interface Slide : NSObject {

    // アウトレット
    IBOutlet UIImageView *imageSlide;    // 画像表示
    IBOutlet UIToolbar *toolbar;    // ツールバー
    IBOutlet UISlider *sliderDuration;    // 長さ変更
    IBOutlet UISegmentedControl *segShuffle;    //シャッフル
    IBOutlet UIButton *btnStartStop;    // 開始ボタン

}

// アクション
- (IBAction)startStop:(id)sender;    // スライドショーの開始停止
- (IBAction)changeOrder:(id)sender;    // スライドショーのシャッフル
- (IBAction)changeDuration:(id)sender;    // スライドショーの長さの変更

- (void)enterOrExitFullScreen;
- (void)enterOrExitFullScreenWithAnimation;

@end

439


●Slide.mソースファイルの編集

- (void)enterOrExitFullScreenWithAnimation {
    // ビューのアニメーションを有効化
    [UIView setAnimationsEnabled:YES];

    // アニメーション定義ブロックの開始
    [UIView beginAnimations:@"FullScreen" context:nil];

    // 現在のビューの中心位置を取得
    CGPoint center = toolbar.center;

    // 現在のビューの矩形と高さを取得
    CGRect frame = toolbar.frame;
    CGFloat height = CGRectGetHeight(frame);

    // 不透明の時は透明化して下に移動
    if (toolbar.alpha == 1.0f) {
        toolbar.alpha = 0.0f;
        center.y += height;
    }
    // 透明の時は不透明化して上に移動
    else {
        toolbar.alpha = 1.0f;
        center.y -= height;
    }

    // 中心位置を変更
    toolbar.center = center;

    // アニメーション実行
    [UIView commitAnimations];

    // ステータスバーのアニメーション
    UIApplication *app = [UIApplication sharedApplication];
    [app setStatusBarHidden:! app.statusBarHidden animated:YES];
}

440


・setAnimationsEnabled:
(UIViewクラス)

+ (void)setAnimationsEnabled:(BOOL)enabled

アニメーションを有効にするかどうかの設定をします。

アニメーションの属性を変更すると、アニメーションが無効になって無視されます。

デフォルトではアニメーションは有効になっています。

enabled:YESの場合はアニメーションは有効に、NOの場合は無効になります。


・beginAnimations:context:
(UIViewクラス)

+ (void)beginAnimations:(NSString *)animationID context:(void *)context

アニメーションブロックを開始します。

アニメーションブロック内でいくつかのプロパティ値を設定することにより、視覚的な変更をアニメーションすることができます。

アニメーションブロックはネスト(入れ子)にすることができます。

setAnimation~クラスメソッドが無い場合、アニメーションブロック内では何も呼び出しません。

アニメーションブロックは、beginAnimations:context:で開始され、commitAnimationクラスメソッドで終了します。

animationID:アニメーションデリゲートメッセージに渡すため、アプリケーションが供給するブロック内のアニメーションの識別子です。
セレクタの設定では、setAnimationWillStartSelector:とsetAnimationDidStopSelector:メソッドを使います。

context:アニメーションデリゲートメッセージに渡すため、アプリケーションが供給するブロック内の追加情報です。
セレクタの設定では、setAnimationWillStartSelector:とsetAnimationDidStopSelector:メソッドを使います。


・center
(UIViewクラス)

@property(nonatomic) CGPoint center

フレームの中心です。

スーパービューの座標系内の中央を指定します。

このプロパティの設定を変更すると、それに応じてframeプロパティの値が変わります。

フレーム矩形を変更すると、drawRect:メソッドの呼び出し無しにレシーバを自動的に再表示します。

フレーム矩形の変更時にdrawRect:メソッドを呼び出したい場合は、UIViewContentModeRedrawにcontentModeプロパティを設定してください。

このプロパティを変更して、アニメーションをすることができます。

アニメーションブロックで、開始にはbeginAnimations:context:クラスメソッド、終了にはcommitAnimationsクラスメソッドを使います。


・CGRectGetHeight
(CGGeometry)

CGFloat CGRectGetHeight (
    CGRect rect
);

矩形の高さを返します。

CGRectデータ構造体の格納している数値の正負に関わらず、この関数は標準かした矩形の高さを返します。

したがって、結果が負の数になることはありません。

rect:調べる矩形を指定します。


・alpha
(UIViewクラス)

@property(nonatomic) CGFloat alpha

レシーバのアルファ値です。

このプロパティを変更して、アニメーションをすることができます。

アニメーションブロックで、開始にはbeginAnimations:context:クラスメソッド、終了にはcommitAnimationsクラスメソッドを使います。


・commitAnimations
(UIViewクラス)

+ (void)commitAnimations

このアニメーションブロックが外側の時、アニメーションブロックの終了を示し、アニメーションを開始します。

現在のアニメーションブロックが外側のアニメーションブロックの場合、アプリケーションの実行ループに返される時にアニメーションが開始します。

アプリケーションはブロックされないため、アニメーションの実行は別のスレッドで行われます。

この場合、複数のアニメーションを互いの上に積み重ねることができます。

アニメーション開始の他の方法については、setAnimationBeginsFromCurrentState:を参照してください。


・setStatusBarHidden:animated:
(UIApplicationクラス)

- (void)setStatusBarHidden:(BOOL)hidden animated:(BOOL)animated

ステータスバーの非表示または表示、任意で変化をアニメーションできます。
(このメソッドはiPhone OS 3.2では非推奨。iPhone OS 3.2では、代わりにsetStatusBarHidden:withAnimation:を使用してください。)

hiddenの値によって、インターフェイスの上部にステータスバーを外または中にフェードするアニメーションを行うことができます。

hidden:YESの場合はステータスバーを非表示に、NOの場合は表示になります。
デフォルト値はNOになります。

animated:YESの場合は非表示状態からアニメーションで変化し、NOではアニメーションしません。


●CGGeometry

CGGeometryリファレンスは、幾何学的な原型とその操作を行う構造体を定義します。

CGPointデータ構造体は、二次元座標系の点を表します。

CGRectデータ構造体は、矩形の位置と大きさを表します。

CGSizeデータ構造体は、幅と高さの大きさを表します。

CGRectデータ構造体に格納される高さと幅は、負の値でも格納できます。

例えば、矩形の原点が (0.0 , 0.0) でサイズが (10.0 , 10.0) のものと、矩形の原点が (10.0 , 10.0) でサイズが (-10.0 , -10.0) のものは全く同一になります。

アプリケーションはCGRectStandardize関数を呼び出して、矩形を標準化する(格納している高さと幅を正の値にする)ことができます。

このリファレンスで述べている全ての関数は、CGRectデータ構造体で入力された場合には暗黙的に標準化してから矩形の計算を行います。

その理由は、CGRectデータ構造体に格納されているデータの読み書きを、アプリケーションが直接行うことを避けるためです。

代わりにここでは、矩形の操作とその特質を取得する関数の使い方を表します。



参考文献

UIView Class Reference

CGGeometry Reference

UIApplication Class Reference

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

フルスクリーン化で使用しているクラス(3)

2010. 06. 20
●NSObjectクラス

NSObjectは、ほとんどのObjective-Cクラス階層のルートクラスです。

NSObjectを通して、オブジェクトはランタイムシステムの基本インターフェイスを継承し、Objective-Cのオブジェクトして動作します。

セレクタ

NSObjectは、Objective-Cランタイムシステムを利用したいくつかの特別なメソッドを持っています。

例えば、特定のメソッドを呼び出す前にメッセージが応答した場合、クラスまたはインスタンに求めることができます。

また、perform~メソッドの一つを使ってメソッドの実装と呼び出しを求めることができます。

関数として呼び出すこともできますが、動的バインディングを回避するので通常は推奨しません。

これらおよび他のNSObjectメソッドは、引数としてSEL型のセレクタを取ります。

効率化のため、コンパイルしたコードでのメソッドは、完全なASCII名で表されません。

代わりに、ランタイムでのセレクタによるメソッドの呼び出しには、コンパイラはユニークな識別子を使います。

セレクタのメソッド名は、@selector()ディレクティブを使って取得できます。

SEL method = @selector(isEqual:);

instanceMethodForSelector:クラスメソッドとmethodForSelector:インスタンスメソッドはIMP型のメソッドの実装を返します。

IMPはidを返し、可変個の引数を取る関数のポインタとして定義されています。
(加えて、全てのメソッドの実装で、2つ『隠し』引数(selfと_cmd)渡します)

typedef id (*IMP)(id, SEL, ...);

この定義は、これらのメソッドが返す関数ポインタのプロトタイプとして利用できます。

それはオブジェクトを返し、オブジェクトの引数を取得するのに十分なメソッドです。

しかしセレクタが異なる引数型を取る、またはid以外を返す場合、その関数が不十分なプロトタイプになります。

欠けたプロトタイプの場合、コンパイラは実数を倍精度、文字を整数とみなすので予測できない実装になります。

したがって、メソッドが異なった(間違った)実行をします。

このような状況を解決するには、必要に応じて自身のプロトタイプを提供することです。

以下の例では、isEqual:メソッドの実装をプロトタイプで提供したtest変数の宣言しています。

testは関数のポインタとして定義され、BOOL値を返し、idの引数(と2つの隠し引数)を取ります。

戻り値はmethodForSelector:で返され、次に同じ関数の型のポインタにキャストされます。

BOOL (*test)(id, SEL, id);
test = (BOOL (*)(id, SEL, id))[target methodForSelector:@selector(isEqual:)];

while ( !test(target, @selector(isEqual:), someObject) ) {
    ...
}

いくつかの場合、変数の宣言とmethodForSelector:が返す関数ポインタをキャストするために、(IMPに似た)型を明確に定義することができます。

以下の例は、この目的のためにEqualIMP型を定義します。

typedef BOOL (*EqualIMP)(id, SEL, id);
EqualIMP test;
test = (EqualIMP)[target methodForSelector:@selector(isEqual:)];

while ( !test(target, @selector(isEqual:), someObject) ) {
    ...
}

いずれにせよ、methodForSelector:の戻り値は適切な関数の型にキャストすることが重要です。

methodForSelector:が返す関数を単に呼び出し、呼び出した結果を目的の型にキャストするだけでは不十分です。

そうすると、結果でエラーがでます。

詳細は『Objective-C Runtime Programming Guide』の『Messaging』を参照してください。


●UIApplicationクラス

UIApplicationクラスは、iPhone OS上でのアプリケーションの動作の制御と調整の、一元的な管理を提供します。

全てのアプリケーションは、UIApplication(またはUIApplicationのサブクラス)のインスタンスを必ず一つ持つ必要があります。

アプリケーションが起動するとUIApplicationMain関数を呼び出し、他のタスクとは独立したシングルトンのUIApplicationオブジェクトを生成します。

その後sharedApplicationクラスメソッドでオブジェクトを呼び出し、アクセスすることができます。

UIApplicationオブジェクトの重要な役割は、入力されたユーザイベントの初期経路の処理を行うことです。

またコントロールオブジェクト(UIControl)によって、アクションメッセージを適したターゲットオブジェクトに送ります。

さらにUIApplicationオブジェクトは、アプリケーションが現在開いている全てのウィンドウ(UIWindowオブジェクト)のリストを保持し、それらを介してアプリケーションのUIViewオブジェクトを取得することができます。

アプリケーションオブジェクトは通常デリゲートを割り当てており、アプリケーションの重要なランタイムイベント(例えば、アプリケーションの起動、メモリ不足の警告、アプリケーションの終了など)の応答を適時通知します。

アプリケーションは、openURL:メソッドを介して電子メールまたは画像ファイルといったリソースを協調処理できます。

例えば、アプリケーションがこのメソッドを使って、電子メールのURLからメールクライアントの起動とメッセージの表示を行うこともできます。

iPhone OS 3.0ではUIApplicationにリモート通知を登録するメソッドが追加され、アンドゥ/リドゥUI(applicationSupportsShakeToEdit)のトリガや、インストールされたアプリケーションでURLを開くかどうかを決定する(canOpenURL:)などがあります。

UIApplicationはデリゲートを定義しており、一つ以上のメソッドを実装したUIApplicationDelegateプロトコルを採用する必要があります。

またUIApplicationとUIApplicationDelegateのプログラムインターフェイスは、デバイス固有の動作を管理することができます。

アプリケーションでデバイスの向きによるインターフェイスの変更や、ユーザイベント入力の一時停止、ユーザの顔を検出する近接センサによるオン/オフなどの制御ができます。

サブクラス化の注意

カスタムイベントとアクションディスパッチを実装するには、UIApplicationをサブクラス化して、sendEvent:またはsendAction:to:from:forEvent:をオーバーライドします。

しかし多くの場合、クラスよりアプリケーションデリゲートを拡張した方が有効です。

UIApplicationをサブクラス化する場合、サブクラスで達成しようとしていることを熟考してみてください。



参考文献

NSObject Class Reference

UIApplication Class Reference

Cocoaリファレンス日本語化計画/NSObject Class Reference

福井高専IT研究会OfficialWiki/UIApplication

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

フルスクリーン化で使用しているクラス(2)

2010. 06. 19
●UIResponderクラス

UIResponderクラスは、イベント処理とその応答に対するオブジェクトのインターフェイスを定義します。

UIApplicationおよび、UIViewを含むそのサブクラスのスーパークラスです。

これらのクラスのインスタンスは、レスポンダオブジェクトまたは単にレスポンダと呼ばれます。

一般的にイベントにはタッチイベントとモーションイベントの2つがあります。

主なタッチのイベント処理メソッドにはtouchesBegan:withEvent:、touchesMoved:withEvent:、touchesEnded:withEvent:、touchesCancelled:withEvent:があります。

これらのメソッドのパラメータはタッチによる(特に新規または変更の)イベントに関連付けされ、マルチタッチシーケンスのフェーズを介して、タッチ操作のイベントの経過について方法と処理をレスポンダオブジェクトに渡します。

指がスクリーンに触れたり、スクリーンをドラッグしたり、スクリーンから離したりした時に、UIEventオブジェクトが生成されます。

イベントオブジェクトは、スクリーンに指を乗せたり離したりした全てのUITouchオブジェクトを含みます。

iPhone OS 3.0では、デバイスのシェイクなど、モーションイベントを生成する機能がシステムに導入されています。

このようなイベント処理のメソッドには、motionBegan:withEvent:、motionEnded:withEvent:、motionCancelled:withEvent:があります。

iPhone OS 3.0ではcanPerformAction:withSender:メソッドが追加され、レスポンダはユーザインターフェイスの命令として、レスポンダチェイン内の直近のNSUndoManagerオブジェクトを返すundoManagerプロパティを有効にすることができます。


●NSSetクラス

NSSet、NSMutableSetとNSCountedSetクラスは、オブジェクトのセットを管理する、オブジェクトのプログラムインターフェイスを宣言します。

NSSetは、集合の数学的概念のサポートを提供します。

セットは数学的観念とNSSetの実装の両方の意味があり、要素の異なる順序無しの集合を指します。

NSSetのサブクラスであるNSMutableSetとNSMutableSetのサブクラスであるNSCountedSetクラスは、内容が変更できるセットを提供します。

NSSetとNSMutableSetはクラスクラスタの一部であり、セットは実際のNSSetまたはNSMutableSetのインスタンスではありません。

むしろインスタンスは、プライベートサブクラスの一つとして属しています。
(利便性のため、厳密なクラスの一員としてではなく、これらのインスタンスいずれかに属する条件のセットを使用します)

とはいえセットのクラスはプライベート、インターフェイスはパブリックで、NSSetとNSMutableSetは抽象的なスーパークラスとして宣言されています。

ただし、NSCountedSetはクラスクラスタの一部ではなく、NSMutableSetのサブクラスとして具現化されています。

NSSetは、静的なオブジェクトのセットのプログラムインターフェイスを宣言します。

静的なセットは生成時に情報を入力し、その後の情報入力の修正はできない規定になっています。

もう一方のNSMutableSetは、動的なオブジェクトのセットのプログラムインターフェイスを宣言します。

動的(または可変)セットは、いつでも情報入力の追加と削除をすることができ、メモリは必要に応じて自動的に確保します。

要素の順序が重要ではなく、セットに含めるオブジェクトの性能を考査した上で、セットの代わりに配列を使うこともできます。
(配列には順序があり、要素によってはセットより遅い場合があります)

セットのオブジェクトは、NSObjectプロトコルメソッドのhashとisEqual:に応答する必要があります。
(詳細は『NSObject』のプロトコルを参照してください)

セット内に可変オブジェクトを格納する場合、可変オブジェクトの内部状態にオブジェクトのhashメソッドが依存しない、またはセット内の可変オブジェクトを修正しないことに注意してください。
(集合内に可変オブジェクトを与えることはできますが、困難なことに注意してください)

セットに追加するオブジェクトはコピーではなく、セットへ追加する前にオブジェクトはretainメッセージを受け取ります。

通常、一時的なセットの生成には、NSSetクラスオブジェクトのset~メソッドの内のひとつを送ります。

これらのメソッドは、(もしあれば)引数を渡して、要素を含むNSSetオブジェクトを返します。

setメソッドは便利なメソッドで、空の可変セットを生成します。

セットクラスはNSCopyingとNSMutableCopyingプロトコルを採用し、もう一方のタイプのセットへの変換を容易に行えます。

NSSetは、セットの要素を問い合わせるメソッドを提供します。

allObjectsは、セット内のオブジェクトを含む配列を返します。

anyObjectは、セット内のいくつかのオブジェクトを返します。

countは、セット内の現在のオブジェクト数を返します。

member:は、指定したオブジェクトと同じセット内のオブジェクトを返します。

また、intersectsSet:はセットの積集合(2つのセット間の共通要素)かを分析、isEqualToSet:は同一性かを分析、isSubsetOfSet:は部分集合(あるセットがもう一つのサブセット)かを分析します。

objectEnumeratorメソッドは、セットの要素の順次走査を提供します。

Mac OS X v10.5以降ではより性能を向上した、Objective-Cを使った高速列挙が使用できます。

NSSetのmakeObjectsPerformSelector:とmakeObjectsPerformSelector:withObject:メソッドは、セット内の個々のオブジェクトにメッセージを送ることができます。

NSSetには『toll-free bridged』の対象としてCore FoundationのCFSet Referenceがあります。

これはFoudationオブジェクトを橋渡ししてCore Foundationの型に変換し、関数やメソッドで呼び出せることを意味します。

したがってNSSet *パラメータを参照する際、CFSetRefに渡し、CFSetRefパラメータとして参照でき、(コンパイラの警告を抑えるために型変換をして)NSSetインスタンスに渡すこともできます。

型変換に関する詳細は『Introduction to Carbon-Cocoa Integration Guide』を参照してください。


●UITouchクラス

UITouchオブジェクトは、スクリーン上の指の存在または動作を特定のイベントとして表します。

UITouchオブジェクトのアクセスは、UIEventオブジェクトを通して、イベント処理を行うレスポンダオブジェクトに渡されます。

UITouchオブジェクトは、タッチの発生したビューまたはウィンドウへのアクセスと、ビューまたはウィンドウのタッチされた特定の位置を取得するメソッドを含みます。

またタッチの発生や、ユーザが2回以上タップしたかどうか、指でのスワイプ(その場合の方向)、タッチの位相、ジェスチャの開始と動きと終了、キャンセルされたかなどを検出できます。

UITouchオブジェクトは、常時マルチタッチのシーケンスを継続しています。

イベントの処理をしている時は、UITouchオブジェクトを絶対に保持してください。

他の行程のタッチについての情報を保持する必要がある場合は、UITouchオブジェクトから情報をコピーしてください。

iPhone OS 3.2で導入されたgestureRecognizersプロパティは、タッチを現在処理している(実際にはUIGestureRecognizerのサブクラスの)インスタンスにジェスチャ認識を返します。



参考文献

UIResponder Class Reference

NSSet Class Reference

UITouch Class Reference

すぎゃーんメモ/NSSetのintersectsSetとNSMutableSetのintersectSet

算数・数学用語 Webilo辞書/積集合とは

算数・数学用語 Webilo辞書/部分集合とは

福井高専IT研究会OfficialWiki/UITouch

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

フルスクリーン化で使用しているクラス(1)

2010. 06. 17
スライドショーのフルスクリーン化で使用したクラスの概要を紹介します。


●UIViewクラス

UIViewクラスは主として抽象的なスーパークラスで、実際に描画やイベント処理を構成するサブクラスを提供します。

また、他のビューを含むUIViewのインスタンスを生成できます。

UIViewオブジェクトはUIWindowオブジェクトの中に配置され、サブビューの階層の中にネスト(入れ子)されています。

ビュー階層の親オブジェクトはスーパービューと呼ばれ、子はサブビューと呼ばれます。

ビューオブジェクトはスーパービュー内に矩形領域を要求し、領域内の全ての描画の責任を持ち、発生するイベントを受け取ります。

姉妹ビューは何の問題も無く重ねることができ、複雑なビュー配置が可能です。

UIViewクラスは、全てのビュータイプの生成に使用する共通メソッドと、プロパティへのアクセスを提供します。

例えばサブクラスが独自の指定イニシャライザを持っていない場合は、ビューの生成にinitWithFrame:メソッドを使用します。

frameプロパティはスーパービューの座標内にある、ビューの原点と座標を指定します。

全てのビューの座標系の原点は左上角になります。

また、ビューの位置とサイズの設定に、centerとboundsプロパティを使用することができます。

centerプロパティは、スーパービューの座標におけるビューの中心点を指定します。

boundsプロパティは、ビューの座標における原点とサイズを指定します。
(ビューの大きさがboundsのサイズより大きいとします)

frameプロパティは、centerとboundsプロパティ値を実際の基盤上の値として計算します。

したがって、これら3つのプロパティの設定は、他の値に作用します。

それは表示や向きが変わった時に、スーパービューの区域内に正確にビューを表示するため、ビューの自動リサイズのプロパティ設定にとって重要です。

特にUIViewのサブクラスでautoresizesSubViewsプロパティは使用され、サブビューを自動的にリサイズするかどうかを指定します。

autoresizingMaskプロパティは、どのようにビューを自動的にリサイズするかを、UIViewAutoresizingの定数で指定します。

UIViewクラスは、ビューの階層を管理するためのメソッドの番号を提供します。

階層内の親ビューの取得はsuperviewプロパティ、子ビューの取得はsubviewプロパティを使用します。

メソッドの番号は『Managing the View Hierarchy』にもあり、サブビューの追加、挿入、移動や、姉妹ビューの前面または背面の配置について記載されています。

UIViewのサブクラスとして、自身で描画するカスタムクラスを生成する場合、指定領域内のビューへ描画するdrawRect:メソッドを実装してください。

このメソッドは、ビューを最初に表示する、またはビューのフレームの一部が無効になり、内容の再描画を要求するイベントが発生した場合に呼び出されます。

通常、形状変更ではビューの再描画は要求されません。

したがってビューの外観を変更し、強制的に再描画をする場合は、setNeedsDisplayまたはsetNeedsDisplayInRect:をビューに送ります。

区域を変更する際に、UIViewContentModeRedrawにdrawRect:メソッドを呼び出すためにcontentModeを設定することもでき、それ以外の場合は、ビューは内容の再描画をせずに拡大縮小とクリッピングをします。

サブクラスは他のビューを含むこともできます。

この場合、指定イニシャライザinitWithFrame:をオーバーライドし、ビュー階層を生成します。

描画前にサブビューのレイアウトをプログラムで行う場合は、ビューにsetNeedsLayoutを送ります。

その後layoutIfNeededを呼び出し、表示する前にlayoutSubviewsを呼び出します。

サブクラスでサブビューの配置をカスタマイズするには、layoutSubviewsをオーバーライドします。

frame、bounds、center、transformプロパティなど、いくつかのプロパティはビューオブジェクトのアニメーションを変更することができます。

アニメーションブロック内のこれらのプロパティを変更すると、現在の状態から新しい状態にアニメーションで変化します。

アニメーションブロックはbeginAnimations:context:クラスメソッドを呼び出すと開始され、アニメーションのプロパティを設定し、そしてアニメーションブロックのcommitAnimationsクラスメソッドを呼び出すと終了します。

アニメーションは別のスレッドで実行され、開始するとアプリケーションの実行ループに戻ります。

他のアニメーションのクラスメソッドには、ブロックのアニメーションの制御として、開始時間、期間、遅延、カーブがあります。

イベント処理の発生とどこで発生したかを知るには、hitTest:withEvent:とpointInside:withEvent:メソッドを使用します。

UIViewクラスは、UIResponderから他のイベント処理メソッドを継承しています。

ビューのイベント処理に関する詳細は『UIResponder Class Reference』を参照してください。

ビューにジェスチャ認識を関連付けする場合は、ジェスチャを解釈できるオブジェクトをビューに作り、addGestureRecognizer:メソッドを呼び出す必要があります。
(ジェスチャ認識は、UIGestureRecognizerのサブクラスのインスタンスで行われます)

ジェスチャ認識を削除するにはremoveGestureRecognizer:メソッドを、ジェスチャ認識に関連付けされているビューを見つけるにはgestureRecognizersプロパティを使用します。

ジェスチャ認識は、iPhone OS 3.2で導入された機能です。

注:iPhone OS 3.0より前では、UIViewのインスタンスは最大1024 x 1024の高さと幅になっています。
iPhone OS 3.0以降では、ビューの最大サイズの制限はありませんが、メモリの消費量によって制限されます。
したがって最適な効果が得られるビューのサイズで、できるだけ小さくする必要があります。
またiPhone OSのバージョンに関係なく、1024 x 1024より大きいサイズのビューを生成する必要がある場合は、CATileLayerの使用を検討してください。



参考文献

UIView Class Reference

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

スライドショーのフルスクリーン化(2)

2010. 06. 17
イベント処理の実装は、新たに作ったUITouchableImageView.mと既存のSlide.mで実装しています。

UITouchableImageView.mにはビューで受け取るイベントの処理として、タップ回数の判別と、タップ回数別に呼び出すメソッドを2種実装し、Slide.mにはシングルタップした場合のフルスクリーン処理のメソッドを実装します。


●UITouchableImageView.mソースファイルの編集

UITouchableImageViewのソースファイルを編集します。
(太字が追加した部分)

#import "UITouchableImageView.h"

@implementation UITouchableImageView

- (id)initWithFrame:(CGRect)frame {

    tapCount = 0;

    if ((self = [super initWithFrame:frame])) {
        // Initialization code
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

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

- (void)singleTap {
    if (tapCount == 1) {    // シングルタップの確認
        // フルスクリーンの開始と解除
        [slide enterOrExitFullScreen];
    }
}

- (void)doubleTap {
    if (tapCount == 2) {    // ダブルタップの確認
        // スライドショーの開始と停止
        [slide startStop:nil];
    }
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    // タッチ情報の集合から1つを取り出す
    UITouch *touch = [touches anyObject];

    // タップカウントを取得
    tapCount = touch.tapCount;

    switch (tapCount) {
        case 1:    // シングルタップの処理
            [self performSelector:@selector(singleTap) withObject:nil afterDelay:0.4f];
            break;
        case 2:    // ダブルタップの処理
            [self performSelector:@selector(doubleTap) withObject:nil afterDelay:0.4f];
            break;
    }
}

@end

436

クラスの追加時に『Subclass of』で『UIView』を選択すると、initWithFrame:からdeallocまでが自動で挿入されます。

この自動挿入部に関しては本書で触れられていないのですが、サンプルコードを見ると、initWithFrame:でtapCount(タップ回数)の初期化を追加しているのと、drawRect:が有効になっています(コードの追加は無し)。

initWithFrame:はビューの初期化を行うメソッドですが、ここではtapCountの初期化を行っているだけです。

drawRect:は画像を描画するためのものですが、コメント欄に

『カスタム描画を行う場合だけ、drawRect:をオーバーライドしてください。
空の実装を行うと、アニメーションの実行中に不具合が出ます。』

とあるので、空実装はせずにコメントアウトしておくのが正しいと思われます。

deallocは特に変更することはないので、そのままにしておきます。

singleTapはシングルタップ時にSlide.mに実装するフルスクリーン切り替えメソッド(enterOrExitFullScreen)を呼び出します。

doubleTapはダブルタップ時にStart/Stop切り替えボタンのメソッド(startStop)を呼び出します。

touchesEnded:withEvent:は、タップした指を離した際に呼ばれるUIResponderのメソッドをオーバーライドして使用しています。

タッチ操作をセット(NSSet)として受け取った後に、tapCountプロパティでタップ回数を抽出し、判定に使用します。

switch文での分岐は、イベントを処理する際の遅延(ディレイ)を設定(0.4秒)するもので、0.4秒間のタップ回数が1回ならシングルタップ、2回ならダブルタップと判別しています。

touchesEnded:withEvent:はタップ毎に呼ばれるので、この処理を設けないとタップを1回した時点でsingleTapが呼び出されてしまい、ダブルタップが認識されないためです。


・initWithFrame:
(UIViewクラス)

- (id)initWithFrame:(CGRect)aRect

指定したフレーム矩形のビューオブジェクトを新規に割り当て、初期化して返します。

戻り値は初期化したビューオブジェクト、オブジェクトが生成できなかった場合はnilを返します。

新しいビューオブジェクトは、使用する前にウィンドウのビュー階層に入っている必要があります。

プログラムでビューオブジェクトを生成した場合、このメソッドはUIViewクラスのイニシャライザを指定します。

インターフェイスの設計にIntereface Builderを使う場合は、nibファイルからビューオブジェクトを読み込んだ後で、このメソッドを呼び出さないでください。

nibファイル内のオブジェクトを復元し初期化するにはinitWithCoder:メソッドを使用し、nibファイル内の属性とビューの属性が一致するように修正してください。

nibファイルからのビューの読み込みに関する詳細情報は、Resources Programming Guideを参照してください。

aRect:フレーム矩形のビューオブジェクトを生成します。
フレームの原点はスーパービューの座標になります。
このプロパティの設定は、centerとboundsプロパティの値によって変わります。


・drawRect:
(UIViewクラス)

- (void)drawRect:(CGRect)rect

渡された矩形の中に、レシーバの画像を描画します。

サブクラスでこのメソッドをオーバーライドする際は、実際にビューを描画します。

サブクラスが他のビューのコンテナの場合、このメソッドでサブクラスをオーバーライドする必要はありません。

デフォルトでは何も実装しません。

UIViewサブクラスを直接カスタムビューにする場合は、superの実装を呼び出す必要はありません。

スーパークラスの実装で実際に描画し、opaqueがYESの場合は、各サブクラスがrectをすべて満たす責任があります。

このメソッドを呼び出すと、レシーバはフレームと境界矩形の座標変換を引き受けて適用することができます。
(描画するクライアントの関数をすべて呼び出す必要があります)

現在描画する画像の内容を取得するUIGraphicsGetCurrentContext関数を使う場合は、同様に左上角が座標の原点になります。

drawRect:メソッドを呼び出している間は画像の内容が変更可能なので、保持しないでください。

rect:描画する限定範囲の矩形を定義します


・touchesEnded:withEvent:
(UIResponderクラス)

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

ビューまたはウィンドウから、指を一本以上離した時にレシーバに伝えます。

このメソッドはデフォルトでは何も実装しません。

オブジェクトがtouchesEnded:withEvent:メッセージを受け取る際には、touchesBegan:withEvent:の実装で確立された状態情報をクリアする必要があります。

マルチタッチはデフォルトでは無効です。

マルチタッチイベントを受け取れるようにするには、一致するビューインスタンスのmultipleTouchEnabledプロパティをYESに設定する必要があります。


・anyObject
(NSSetクラス)

- (id)anyObject

レシーバの中のオブジェクトを一つ、またレシーバにオブジェクトが含まれていない場合はnilを返します。

返されるオブジェクトは、レシーバの都合の良いものが選ばれます。
(この選抜はランダムであることが保証されていません)


・tapCount
(UITouchクラス)

@property(nonatomic, readonly) NSUInteger tapCount

指でタップした回数を返します。(読み取り専用)

このプロパティの値は整数で示され、予め定義した期間に一定の位置を、ユーザが指でタップした回数を表します。

特定のビューまたはウィンドウで、ユーザのシングルタップ、ダブルタップ、またはトリプルタップを決定するには、このメソッドで返される値で評価してください。


・performSelector:withObject:afterDelay:
(NSObjectクラス)

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

デフォルトモードで使われているレシーバの現在のスレッドの遅延後、メソッドを呼び出します。

このメソッドは、実行ループの現在のスレッドでタイマーを設定し、aSelectorメッセージを実行します。

タイマーはデフォルトモード(NSDefaultRunLoopMode)で実行されるように構成されています。

タイマーが発火すると、スレッドは実行ループの待ち行列(キュー)のメッセージを取り出し、セレクタの実行を試みます。

デフォルトモードでの実行ループの実行ができれば成功で、そうでなければ実行ループがデフォルトモードになるまでタイマーは待機します。

デフォルトモード以外の実行ループのモードでメッセージを受け取りたい場合は、代わりにperformSelector:withObject:afterDelay:inModes:を使用してください。

メインスレッドでのセレクタの実行を確実に行いたい場合は、代わりにperformSelectorOnMainThread:withObject:waitUntilDone:またはperformSelectorOnMainThread:withObject:waitUntilDone:modes:メソッドを使用してください。

待ち行列のメッセージを取り消す場合は、cancelPreviousPerformRequestsWithTarget:またはcancelPreviousPerformRequestsWithTarget:selector:object:メソッドを使用してください。

このメソッドはセレクタの実行が終わるまで、レシーバとanArgumentパラメータを保持します。

aSelector:呼び出すメソッドを識別するセレクタです。
このメソッドは重要な戻り値を必要とせず、タイプIDの単一引数を必要とするか、または引数がありません。
SEL型の詳細は『Selectors』を参照してください。

anArgument:メソッドを呼び出す際に渡す引数を指定します。
引数を取らない場合はnilを渡します。

delay:メッセージを送る前の最小時間を指定します。
やむを得ずセレクタの直後に実行したい場合、遅延を0に指定してください。
スレッドの実行ループでセレクタが待ち行列に入るまでに、できるだけ早く実行してください。


●Slide.hヘッダファイルの編集

本書では触れられていませんが、Slide.hヘッダファイルにenterOrExitFullScreenの宣言するのを忘れないでください。
(太字が追加した部分)

#import <Foundation/Foundation.h>

@interface Slide : NSObject {

    // アウトレット
    IBOutlet UIImageView *imageSlide;    // 画像表示
    IBOutlet UIToolbar *toolbar;    // ツールバー
    IBOutlet UISlider *sliderDuration;    // 長さ変更
    IBOutlet UISegmentedControl *segShuffle;    //シャッフル
    IBOutlet UIButton *btnStartStop;    // 開始ボタン

}

// アクション
- (IBAction)startStop:(id)sender;    // スライドショーの開始停止
- (IBAction)changeOrder:(id)sender;    // スライドショーのシャッフル
- (IBAction)changeDuration:(id)sender;    // スライドショーの長さの変更

- (void)enterOrExitFullScreen;

@end

437


●Slide.mソースファイルの編集

最期にSlide.mにフルスクリーンの切り替えメソッドenterOrExitFullScreenを追加します。

- (void)enterOrExitFullScreen {
    // ツールバーの可視状態を変更
    toolbar.hidden =! toolbar.hidden;

    // アプリケーションのインスタンスを取得
    UIApplication *app = [UIApplication sharedApplication];

    // ステータスバーの可視状態を変更
    app.statusBarHidden =! app.statusBarHidden;
}

438


フルスクリーンにするということは、すなわちツールバーとステータスバーを非表示にすることになります。

ツールバーの非表示にはUIToolbarのスーパークラスであるUIViewのhiddenプロパティを使用します。

ステータスバーの非表示には、UIApplicationのstatusBarHiddenプロパティを使用するため、その前にアプリケーションのインスタンスを取得しています。

どちらのプロパティもブール値ですので、『!』で反転させて『=』で代入して切り替えています。
(比較演算子で非等価を表す『!=』ではありません)


・hidden
(UIViewクラス)

@property(nonatomic, getter=isHidden) BOOL hidden

レシーバを非表示にするブール値を決定します。

レシーバを非表示にする場合はYES、そうでない場合はNOになります。

デフォルトの値はNOです。

非表示はウィンドウからビューが見えなくなるだけでなく、入力イベントも受け付けません。

しかしスーパービューのサブビューリストには残っており、通常通り自動リサイズはされます。

サブビューのビューを隠すとサブビューを隠す効果は子孫のビューにも反映される場合があります。

この効果は暗黙的なもので、レシーバの子孫の非表示状態は変更しません。

ウィンドウの現在のファーストレスポンダーのビューを隠すと、次に有効なキービューが新たなファーストレスポンダーになります。

このプロパティの値はレシーバの状態のみに反映し、ビュー階層の先祖のレシーバの状態には影響しません。

したがって、先祖が非表示なためにレシーバが非表示になっている場合は、このプロパティをNOにすることができます。


・sharedApplication
(UIApplicationクラス)

+ (UIApplication *)sharedApplication

シングルトンのアプリケーションインスタンスを返します。

アプリケーションインスタンスはUIApplicationMain関数で生成されます。


・statusBarHidden
(UIApplicationクラス)

@property(nonatomic, getter=isStatusBarHidden) BOOL statusBarHidden

ステータスバーを非表示にするブール値を決定します。

ステータスバーを非表示にする場合はYES、そうでない場合はNOになります。



参考文献

UIView Class Reference

UIResponder Class Reference

NSSet Class Reference

UITouch Class Reference

NSObject Class Reference

UIApplication Class Reference

iOSアプリケーションプログラミングガイド

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

スライドショーのフルスクリーン化(1)

2010. 06. 15
基礎からのiPhone SDK 改訂版』の『スライドショーの作成』で作ったスライドショーアプリケーションをフルスクリーン化(ステータスバーとツールバーの非表示化)します。

イメージビューをタップするとフルスクリーンをON/OFFし、ダブルタップするとスライドショーのSTART/STOPします。


●イメージビューのサブクラスの作成

UIImageViewにはアクションが設定できないので、オーバーライドしてサブクラスでイベント処理を追加します。

XcodeでUIImageViewのサブクラスとして、UITouchableImageViewという名前のファイルを作ります。

『グループとファイル』ペインの『Classes』を右クリックして、『追加』→『新規ファイル...』を選択します。

新規ファイルのテンプレートで左側ペインの『iPhone OS』下の『Cocoa Touch Class』を選択、右上ペインの『Objective-C class』を選択し、右下ペインの『Subclass of』で『UIView』をプルダウンメニューから選びます。

423

次へ進んで、ファイル名を『UITouchableImageView』とします。


●UITouchableImageView.hヘッダファイルの編集

続いて、生成したUITouchableImageViewのヘッダファイルを編集します。
(太字が追加した部分)

#import <UIKit/UIKit.h>
#import "Slide.h"

@interface UITouchableImageView : UIImageView {
    NSUInteger tapCount;    // タップカウント
    IBOutlet Slide *slide;    // Slideへのアウトレット
}

@end

431

まず、イメージビューをタップした時に実際に対応するメソッドはSlideクラスにあるので、Slide.hをインポートします。

新規ファイルのテンプレートでサブクラスをUIImageとしましたが、実際にはUIImageViewなので、クラス宣言の@interfaceのスーパークラスをUIImageからUIImageViewに変更します。

tapCountはタップ数を一時的に保持し、フルスクリーンのON/OFFと、スライドショーのSTART/STOPの振り分けに使用します。

前述の通り実処理をSlideクラスで行うので、アウトレットとしてslideを設定しています。

ヘッダファイルの編集が終わったら保存し、ResourcesのMainWindow.xibをダブルクリックしてnibファイルの編集をします。


●nibファイルの編集

本書では『Interface Builder上で接続も行ってください』の一言で済まされていますが、サンプルコードを見ても悩んでしまったので、細かく説明します。

Documentウィンドウを開くと下図のようになっています。

426

最初にUIImageViewクラスのイメージビューを、UITouchableImageViewに変更します。

DocumentウィンドウでImage Viewを選択した状態で、InspectorウィンドウのIdentityパネル、『Class Identity』の『Class』プルダウンメニューから『UITouchableImageView』を選択します。

427

そうするとDocumentウィンドウは下図のようになります。

428

次にDocumentウィンドウでSlideを右クリックしてConnectionsパネルを開きます。

429

『Referencing Outlets』の『slide App Delegate』右側の◉をクリックして、Viewウィンドウのイメージビュー、もしくはDocumentウィンドウのTouchable Image Viewに接続します。

430

続いてTouchable Image ViewのAttributesを編集します。

ViewのModeとDrawing、Interactionが、デフォルトとサンプルコード、本書の内容が異なっています。

デフォルトの内容は下図のようになっています。

432

サンプルコードの内容は下図のようになっています。

433

各コードの差異は下表のようになります。

View  デフォルトサンプル
コード
本書今回の
設定
MediaScale To Fillアスペクトを無視して拡大   
Aspect Fillアスペクトを保持して拡大
(余白無し) 
 
DrawingOpaque背景を不透明にする   
Clear Context
Before Drawing
描画前に内容をクリアする   
Clip Subviewsサブビューをクリッピングする  
Autoresize Subviewsサブビューを自動リサイズする
InteractionUser Interaction
Enabled
ユーザ操作を有効にする 
Multiple Touchマルチタッチを有効にする   

Mediaはアスペクトを保持して拡大したいので、Aspect Fillにします。
(余白が出てもいいので全体を表示したい場合は、Aspect Fitを選択して下さい)

DrawingのOpaqueは、不透明にするかどうかで、アニメーションなどで透明度を変化させる必要がなければ、チェックを入れておいた方が描画が速くなると何処かで読んだ記憶があります。
(うろ覚えですが)

今回はサンプルコードと本書の双方でチェックが外れているので、外しておきます。

Clear Context Before Drawingは事前の描画内容をクリアしてから、次の描画をするかどうかのチェックですが、今回はあまり関係がない(この状態でも動作に影響しない)ので外しておきます。

Clip Subviewsは、サブビューがはみ出る場合に範囲内に納めるものなので、チェックしておいた方がいいのかも。

※2010.8.7補足
サブビューが親ビューからはみ出ている場合、はみ出た部分は縮小されるのではなく、カットオフされるようです。
また、チェックするとカットオフの為の余計な演算が入ることになるので、通常はデフォルトのままチェックを入れない方が良さそうです。

User Interaction Enabledは、そのためにサブクラスとして実装しているので、当然チェックします。

Multiple Touchは、本書中で今回は必要無いとしていますのでチェックしません。

434



参考文献

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る

スライドショーの画像配列をXML化

2010. 06. 13
基礎からのiPhone SDK 改訂版』の『スライドショーの作成』で作ったスライドショーアプリケーションに改造を施していきます。

今回はソースコードに直書きしていた画像配列を、プロパティリストとしてソースコードから分離する改良です。


●本書とサンプルコードの相違点

出版社のサイトにあるサンプルコードと本書の内容が異なるので、相違点を説明します。

本文中では、画像配列を生成するユーザ定義メソッドのanimationImagesをプロパティリスト用に書き換えていますが、サンプルコード(展開先は『iPhoneBG2/SampleCode/60.02.Slide ( 配列リソース )』)ではanimationImagesメソッドをそのままにし、プロパティリスト用のメソッドとして新たにsetupImageArrayFromResourceを追加、同メソッドをモード変更のchangeOrder:で呼び出す構造になっています。

しかしこの構造では、

・ソースコードから画像配列を分離しきれていない
・ほぼ同内容のメソッドが2つ存在する
・モード変更しないとsetupImageArrayFromResourceが呼び出されない

という問題がありますので、本書のようにanimationImagesを書き換えるのが正解となります。


●プロパティリストの作成

プロパティリストはKeyとValueを対として情報を格納するもので、XML形式で記述します。

テンプレートで自動生成される、アプリケーション情報を格納するinfo.plistが特に有名です。
(info.plistのあまり詳しくない説明は『Xcodeのプロパティリストエディタ』を参照してください)

今回はスライドショーで使用する画像をプロパティリストとしてXMLで記述し、ソースコードから分離して独立性を高めるのが目的です。

プロパティリストの生成は、XcodeのResourcesフォルダを右クリックしてコンテキストメニューの『追加』→『新規ファイル...』を実行します。

420

左ペインの『Mac OS X』下にある『Resource』を選択し、右上ペインの中から『Property List』を選択して次へ進み、ファイル名を『imageNames.plist』とします。

『Root』KeyのTypeを『Array』とし、アイテムを追加していきます。

Key名とTypeはそのまま、Valueに使用する画像ファイル名を入力します。

421


●animationImagesの修正

本書では正確には『animationImagesFromResource』とメソッド名を変更していますが、呼び出している他のメソッドも書き換える必要が出てくるので、ここではanimationImeafesそのままにしています。

メソッド名を変える場合は、awakeFromNibとchangeOrder:の記述も変更してください。
(同名のanimationImagesプロパティは変更しないように注意してください)

(太字が追加・修正した部分)

- (NSArray *)animationImages {
    // XMLファイルのパスを取得
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *path = [bundle pathForResource:@"imageNames" ofType:@"plist"];

    // XMLファイルを読み込んで配列を生成
    NSArray *imgNames = [NSArray arrayWithContentsOfFile:path];

    int iCount = [imgNames count];
    int i;

    // 画像を格納する空の配列を生成
    NSMutableArray *imgs = [NSMutableArray array];

    for (i = 0; i < iCount; i++) {
        // ファイル名を配列から取り出して画像のインスタンスを生成
        NSString *imgName = [imgNames objectAtIndex:i];
        UIImage *img = [UIImage imageNamed:imgName];

        // 画像のインスタンスを配列に追加
        [imgs addObject:img];
    }

    // 表示順が画像順かシャッフルか判定
    if (segShuffle.selectedSegmentIndex ==1) {
        int i;
        // 配列の個数を取得
        int count = [imgs count];
        // 現在の日時で乱数のシード値を変更
        srand([[NSDate date] timeIntervalSinceReferenceDate]);

        for (i = 0; i < 10; i++) {
            // 乱数の生成
            NSUInteger pos1 = rand() % count;
            NSUInteger pos2 = rand() % count;
            // 要素の入れ替え
            [imgs exchangeObjectAtIndex:pos1 withObjectAtIndex:pos2];
        }
    }
    // 配列を返す
    return(imgs);
}

422

改造した部分の内容は、プロパティリストのパスを指定して読み込み、画像のファイル名を配列として取得、その画像ファイル名から画像のインスタンスを可変配列に一つずつ追加して行くというものです。

最初にプロパティリストのパスを取得します。

mainBundleメソッドで現在実行しているアプリケーションのディレクトリ位置を確認し、pathForResource:ofType:メソッドでファイル名と拡張子からファイルを特定します。

mainBundleとpathForResource:ofType:メソッドの詳細については『Foundation(バンドル)』を参照してください。

次にプロパティリストの内容(画像ファイル名)の固定配列imgNamesを生成します。

imgNames配列から要素数を取得し、forループで空の可変配列imgsに画像のインスタンスを生成しながら追加していきます。

objectAtIndex:メソッドで画像ファイル名を固定配列imgNamesから取得、そのファイル名からimageNamed:メソッドで画像オブジェクトを取得し、addObject:メソッドで可変配列imgsに追加となっています。

imageNamed:メソッドの詳細については『スライドショーのメソッド(1)』を参照してください。


・arrayWithContentsOfFile:
(NSArrayクラス)

+ (id)arrayWithContentsOfFile:(NSString *)aPath

指定したパスのファイルの内容から、含まれている配列を生成して返します。

ファイルが開けない、またはファイルの中に配列が含まれていない場合はnilを返します。

aPathで指定したファイルの中に含まれる配列はプロパティリストオブジェクト(NSString、NSData、NSArray、またはNSDictionaryオブジェクト)のみ認識できます。

aPath:writeToFile:atomically:メソッドで生成される文字列を含む配列のファイルパスを指定します。


・array
(NSArrayクラス)

+ (id)array

空の配列を生成して返します。

このメソッドはNSArrayの可変サブクラスで使用されます。


・objectAtIndex:
(NSArrayクラス)

- (id)objectAtIndex:(NSUInteger)index

indexの位置にあるオブジェクトを返します。

indexが配列の要素数より大きい(countで返される値以上の)場合、NSRangeExceptionが発生します。


・addObject:
(NSMutableArrayクラス)

- (void)addObject:(id)anObject

レシーバの最後に指定したオブジェクトを挿入します。

重要:anObjectがnilの場合、NSInvalidArgumentExceptionが発生します。

anObject:レシーバの内容の最後に追加するオブジェクトを指定します。
この値をnilにしてはいけません。



参考文献

NSArray Class Reference

NSMutableArray Class Reference

基礎からのiOS SDK基礎からのiOS SDK
(2010/10/09)
鶴薗 賢吾、松浦 健一郎 他

商品詳細を見る






Bose QuietComfort 25
Calendar
05 | 2010/06 | 07
Sun Mon Tue Wed Thu Fri Sat
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 - - -
Recent Articles
iTunes


Swift
Categories
Tips
WACOM


Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
BOSE

Bose QuietComfort 25
ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
SQLite
OpenGL ES
Amazon


Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Profile

Author:水月杏香
永遠の初心者プログラマ。

QR Code
QR
Visitors