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

2010. 07. 05
前回、UIViewクラスでiPhone OS 4.0で追加された『Animating Views with Blocks』と分類と分類されているメソッドを紹介しましたが、非推奨メソッドを具体的にどのように置換するのかまでは説明されていません。

iPhone Dev Centerで追加メソッドを検索したところ、『A Short Practical Guide to Blocks』というそれらしい文書が出てきたので紹介します。

が、訳した本人も意味不明な部分がいつもより多いので、原文を読むことを推奨します。


●ブロックの簡易実践ガイド

これまでブロックは、スクリプトやRuby、Python、Lisp(クロージャやラムダ計算と呼ばれるもの)の一部として存在していました。

Mac OS X v10.6とiPhone OS 4.0では、ブロックは強力なC言語の機能としてCocoaアプリケーション開発の一部になっています。

ブロックの構文と保管の詳細は一見不可解に思われるかもしれませんが、実際にはかなり簡単にプロジェクトのコードに組み込まれたブロックを見いだすことができます。

以下にブロックの機能の概観の高レベルな解説と、典型的な使用方法を示します。

ブロックの詳細については『Blocks Programming Topics』を参照してください。

・ブロックを使う理由

ブロックは作業の構成単位(またはコードの一区切りを表す、より抽象的な用語)をカプセル化したオブジェクトで、いつでも実行することができます。

ブロックは基本的に移植性と匿名性を持つ関数で、メソッドや関数に引数を渡したり返してもらうことができます。

ブロック自身が型指定された引数のリストと、推論できるか宣言された戻り値の型があります。

また変数にブロックを割り当てたり、関数のように呼び出すこともできます。

脱字記号(^)は、ブロックを示す構文上の目印として使用します。

例えば、以下に2つの整数の変数を渡して整数の戻り値を返すブロックのコードを示します。

int (^Multiply)(int, int) = ^(int num1, int num2) {
    return num1 * num2;
};
int result = Multiply(7, 4); // result is 28

メソッドまたは関数の引数のように、ブロックはコールバックの型で、メソッドまたは関数に限定された委任と考えることができます。

ブロックに渡してコードを呼び出すことで、メソッドまたは関数の動作をカスタマイズすることができます。

呼び出されたメソッドまたは関数はいくつかの作業を実行し、適した時期に(ブロックを経て)呼び出したコードにコールバックし、要求された追加情報やアプリケーション特有の動作を入手します。

関数やメソッドのパラメータとしてのブロックの利点は、呼び出し元がコールバックのコードを提供できる点です。

なぜなら、このコードはメソッドまたは関数と分離して実装する必要が無く、コードの実装を単純にし簡単に理解することができます。

例としてNSNotificationの様々な通知を考えてみます。

従来の手法では、オブジェクトは通知のオブザーバ(観測者)として自身を追加し、(addObserver:~メソッドのセレクタで識別される)別のメソッドで通知の処理を実装します。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(keyboardWillShow:)
        name:UIKeyboardWillShowNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)notification {
// notification-handling code goes here
}

addObserverForName:object:queue:usingBlock:メソッドを使うと、通知の発動と処理コードのメソッドを一つにまとめることができます。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification
        object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif)
{
            // same notification-handling code goes here
    }];
}

コールバックの他の手法を使ったブロックのさらに有益な利点は、ローカルの構文スコープ(静的スコープ)内でブロックがデータを共有できることです。

メソッド内でブロックを定義するメソッドを実装する場合、ブロックはローカル変数と(スタック変数を含む)メソッドのパラメータだけでなく、関数とグローバル変数、インスタンス変数にアクセスすることができます。

このアクセスは読み込み専用ですが、__block修飾子で宣言されている変数はブロック内で値を変更することができます。

メソッドまたは関数に含まれているブロックが戻るとローカルスコープが破棄され、ローカル変数はブロックオブジェクトの一部としてブロックが参照されている間は保持されます。

・システムフレームワークAPIのブロック

ブロックを使用する明白な動機の一つは、システムフレームワークのメソッドと関数の数が増えたので、パラメータをブロックで取得するためです。

フレームワークメソッドのブロックを使用した、6つの識別例を以下に示します。

・完了ハンドラ(Completion handlers)
・通知ハンドラ(Notification handlers)
・エラーハンドラ(Error handlers)
・列挙(Enumeration)
・ビューアニメーションとトランジション(View animation and transitions)
・並べ替え(Sorting)

以下の項で各例について説明します。

ただし、ここではフレームワークメソッドのブロック宣言について簡単な概要を説明するだけです。

NSSetクラスの以下のメソッドを考えてみます。

- (NSSet *)objectsPassingTest:(BOOL (^)(id obj, BOOL *stop))predicate

ブロック宣言は、メソッドがブロック(の各列挙項目)に渡す動的に型付けされたオブジェクトと参照するブール値を示し、ブロックはブール値を返します。
(これらのパラメータと戻り値は、実際には『列挙(Enumeration)』で扱います)

ブロックを指定する時は、脱字記号(^)で開始して引数リストを括弧で括ります。

次にブロックのコード自身を中括弧({})で囲みます。

[mySet objectsPassingTest:^(id obj, BOOL *stop) {
    // code goes here; end by returning YES or NO....
}];

・完了とエラーハンドラ

完了ハンドラは、フレームワークメソッドまたは関数がタスクを完了した時に、クライアントにいくつかのアクションの実行を許可するコールバックです。

多くの場合、クライアントはフリーの状態またはユーザインターフェイスの更新に完了ハンドラを使用します。

いくつかのフレームワークメソッドは、ブロックとして完了ハンドラ(または委任メソッドや通知ハンドラ)を実装できます。

UIViewクラスは、完了ハンドラブロックのパラメータを持つ、アニメーションとビュートランジションのクラスメソッドがいくつかあります。
(これらのメソッドの概要は『ビューアニメーションとトランジション(View animation and transitions)』を参照してください)

例としてリスト1-1に、animateWithDuration:animations:completion:メソッドの実装を示します。

この例の完了ハンドラは、アニメーションをするビューの背景を独自のロケーションと透明度(アルファ)値でリセットした後、数秒でアニメーションを終了します。

リスト1-1 完了ハンドラブロック

- (IBAction)animateView:(id)sender {
    CGRect cacheFrame = self.imageView.frame;
    [UIView animateWithDuration:1.5 animations:^{
        CGRect newFrame = self.imageView.frame;
        newFrame.origin.y = newFrame.origin.y + 150.0;
        self.imageView.frame = newFrame;
        self.imageView.alpha = 0.2;
    }
            completion:^ (BOOL finished) {
                if (finished) {
                    // revert image view to original...
                    sleep(3);
                    self.imageView.frame = cacheFrame;
                    self.imageView.alpha = 1.0;
                }
            }];
}

いくつかのフレームワークメソッドは、完了ハンドラに似ているブロックパラメータのエラーハンドラを持っています。

メソッドがそれらを呼び出す(NSErrorオブジェクトに渡す)時は、いくつかのエラー状態のため、タスクを完了することはできません。

通常は、エラーに関してユーザに通知するように、エラーハンドラを実装します。

・通知ハンドラ

NSNotificationCenterメソッドのaddObserverForName:object:queue:usingBlock:は、その時点での通知のオブザーバ(観測者)としてのオブジェクトを追加する、通知のハンドラを実装することができます。

通知ハンドラとは別のメソッドを実装する必要はありません。

リスト1-2では、通知のブロックハンドラを定義し、どのようにこのメソッドを呼び出すかを示しています。

通知ハンドラメソッドは、NSNotificationオブジェクトと同様に渡されます。

また、メソッドはNSOperationQueueのインスタンスを受け取るので、アプリケーションは状況に応じて実行するブロックハンドラを指定することができます。

リスト1-2 オブザーバとしてのオブジェクトの追加と、ブロックを使った通知の処理

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    opQ = [[NSOperationQueue alloc] init];
    [[NSNotificationCenter defaultCenter] addObserverForName:@"CustomOperationCompleted"
            object:nil queue:opQ
        usingBlock:^(NSNotification *notif) {
        NSNumber *theNum = [notif.userInfo objectForKey:@"NumberOfItemsProcessed"];
    NSLog(@"Number of items processed: %i", [theNum intValue]);
    }];
}

・列挙

Foundationフレームワークの集合クラス(NSArray、NSDictionary、NSSet、NSIndexSet)は、集合の特定の種類の列挙を行うメソッドの宣言と、各列挙項目をクライアントの供給するコードで処理またはテストするためのブロックの指定を行います。

言い換えると、メソッドは高速列挙の構築と同等のことを行います。

for (id item in collection) {
    // code goes here that would go in block
}

ブロックの取得には、一般的に二つの列挙メソッドの形式があります。

一つ目は、enumerateという名前で始まる値を返さないメソッドです。

これらのメソッドのブロックは、各列挙項目にいくつかの作業を行います。

二つ目のブロックパラメータのメソッドは、passingTest:の前に整数またはNSIndexSetオブジェクトを返すようなメソッドです、

これらのメソッドのブロックが各列挙項目のテストを行い、項目がテストをパスした場合はYESを返します。

テストにパスしたオブジェクトまたは元の集合のオブジェクトは、整数またはインデックスセットが識別されます。

リスト1-3では、これら各種類のメソッドを呼び出すコードを示しています。

最初のメソッド(『passing test』メソッド)のブロックは、配列内の全ての文字列が特定の接頭辞を持つ場合にYESを返します。

コードはその後、メソッドが返すインデックスセットを使って一時的な配列を生成します。

二番目のメソッドのブロックは、一時的な配列の各文字列から接頭辞を取り除き、新しい配列に追加しています。

リスト1−3 二つのブロックを使用した列挙配列の処理

NSString *area = @"Europe";
NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];
NSMutableArray *areaArray = [NSMutableArray arrayWithCapacity:1];
NSIndexSet *areaIndexes = [timeZoneNames indexesOfObjectsWithOptions:NSEnumerationConcurrent
            passingTest:^(id obj, NSUInteger idx, BOOL *stop) {
    NSString *tmpStr = (NSString *)obj;
    return [tmpStr hasPrefix:area];
}];

NSArray *tmpArray = [timeZoneNames objectsAtIndexes:areaIndexes];
[tmpArray enumerateObjectsWithOptions:NSEnumerationConcurrent|NSEnumerationReverse
        usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            [areaArray addObject:[obj substringFromIndex:[area length]+1]];
}];
NSLog(@"Cities in %@ time zone:%@", area, areaArray); for (id item in collection) {
    // code goes here that would go in block
}

(この例では使われていませんが)各列挙メソッドのstopパラメータは、参照に戻るメソッドに列挙を停止するように伝えて、ブロックにYESを渡すことができます。

この時、いくつかの基準に一致する集合の最初の項目を検索します。

それは集合を表していないにも関わらず、NSStringクラスはブロックパラメータとして名前がenumerate:で始まる二つのメソッド、enumerateSubstringsInRange:options:usingBlock:とenumerateLinesUsingBlock:を持っています。

最初のメソッドは、指定した長さ(行、段落、単語、文章など)のテキスト単位で文字列を列挙し、二つ目のメソッドは行のみを列挙します。

リスト1-4では、最初のメソッドの使い方を説明します。

リスト1-4 ブロックを使った文字列内の部分文字列の一致検索

NSString *musician = @"Beatles";
NSString *musicDates = [NSString stringWithContentsOfFile:
    @"/usr/share/calendar/calendar.music"
    encoding:NSASCIIStringEncoding error:NULL];
[musicDates enumerateSubstringsInRange:NSMakeRange(0, [musicDates length]-1)
    options:NSStringEnumerationByLines
    usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        NSRange found = [substring rangeOfString:musician];
        if (found.location != NSNotFound) {
            NSLog(@"%@", substring);
        }
    }];

・ビューアニメーションとトランジション

UIViewクラスはiPhone OS 4.0で、ブロックを使ったアニメーションとビュートランジションのいくつかのクラスメソッドを導入しました。

ブロックパラメータには二種類あります(全てのメソッドが双方を取る訳ではありません)。

・アニメーションするビュープロパティを変更するブロック
・完了ハンドラ

リスト1-5では、両種類のブロックパラメータを持つメソッドanimateWithDuration:animations:completion:の呼び出しを示します。

この例では、(アルファをゼロに指定して)ビューを消すアニメーションと、完了ハンドラでスーパービューからビューを取り除いています。

リスト1-5 ブロックを使ったビューの簡単なアニメーション

[UIView animateWithDuration:0.2 animations:^{
        view.alpha = 0.0;
    } completion:^(BOOL finished){
        [view removeFromSuperview];
    }];

他のUIViewクラスメソッドはフリップとカールを含む二つのビュー間のトランジションを行います。

リスト1−6では、transitionWithView:duration:options:animations:completion:を呼び出して左フリップのトランジションでサブビューを置き換えるアニメーションの例を示します。
(完了ハンドラは実装していません)

リスト1-6 二つのビュー間のフリップトランジションの実装

[UIView transitionWithView:containerView duration:0.2
        options:UIViewAnimationOptionTransitionFlipFromLeft    animations:^{
        [fromView removeFromSuperview];
        [containerView addSubview:toView]
    }
    completion:NULL];

・並べ替え

Foundationフレームワークは、NSComparator型の二つの項目の比較を宣言します。

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

NSComparatorは二つのオブジェクトを取得するブロックタイプでNSComparisonResult値を返します。

それはNSSortDescriptor、NSArray、NSDictionaryのメソッドのパラメータで、これらのクラスのインスタンスの並べ替えに使用されます。

リスト1-7で使用例を示します。

リスト1-7 NSComparatorブロックを使った配列の並べ替え

NSArray *stringsArray = [NSArray arrayWithObjects:
            @"string 1",
            @"String 21",
            @"string 12",
            @"String 11",
            @"String 02", nil];
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
NSComparator finderSort = ^(id string1, id string2) {
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    return [string1 compare:string2 options:comparisonOptions range:string1Range
locale:currentLocale];
};
NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingComparator:finderSort]);

この例は『Blocks Programming Topics』から引用しています。

・ブロックと同期

ブロックは移植性と匿名性を作業単位でカプセル化したオブジェクトで、後から非同期で実行することができます。

この事はブロックが、Grand Central Dispatch(GCD)とNSOperationQueueクラスという二つの推奨される並列処理技術の中心的な機能を持っていることを意味します。

・GCDの二つの中心的な機能である、dispatch_sync(同期ディスパッチ)またはdispatch_async(非同期ディスパッチ)では、ブロックを二番目の引数として取得します。

・NSOperationQueueは、オブジェクトのスケジュールタスクを並列または依存関係で定義されたオーダーで行います。

GCDとNSOperationQueue、NSOperationの詳細については『Concurrency Programming Guide』を参照してください。



参考文献

A Short Practical Guide to Blocks

Wikipedia/クロージャ

Wikipedia/ラムダ計算

Wikipedia/静的スコープ

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

商品詳細を見る






Wave SoundTouch music system IV
0 Comments
Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
06 | 2017/07 | 08
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 31 - - - - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

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

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

Wave SoundTouch music system IV
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
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR