Swipes(2)~Gesture Recognizer

2012. 02. 29
Swipes(1)~イベント処理メソッド』では、スワイプ操作を旧来のタッチイベント処理メソッド(touchesBegan:withEvent:touchesMoved:withEvent:touchesEnded:withEvent:touchesCancelled:withEvent:)を使用して処理しました。

原著であるApress社のiOS 3.x版のサンプルコード『13 Swipes』とiOS 4.x版のサンプルコード『15 - Swipes』は同じですが、iOS 4.x版には『15 - Swipes 2』として同内容をGesture Recognizerを使用する方法に書き換えています。

後程マルチスワイプに対応するにあたり、タッチイベント処理メソッドを使うiOS 3.x版と、Gesture Recognizerを使うiOS 4.x版の両方を試すので、オリジナルのSwipesプロジェクトを別のフォルダなどにコピーして残しておくと便利です。

Gesture RecognizerはiOS 3.2で導入されたジェスチャ認識を支援する技術で、特に複雑なマルチタッチジェスチャの検出をタッチイベント処理メソッドよりも簡易に行うことが可能になります。
(『iOSイベント処理ガイド』参照)


●スワイプジェスチャ認識用定数

iOS 4.x版では、ジェスチャをスワイプと認識するための定数kMinimumGestureLengthとkMaximumVarianceがヘッダファイルではなくソースファイルで定義されています。

ただしGesture Recognizerを利用する場合は、これらの定数は使用しません。


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

Gesture Recognizerでジェスチャを認識するコードを記述するためviewDidLoadメソッドのコメントアウト解除と追記を行い、それに伴って不要となるtouchesBegan:withEvent:touchesMoved:withEvent:メソッドの削除を行います。
またGesture Recognizerで水平および垂直スワイプを認識した際に、呼び出してラベルを更新するメソッドを2つ追加します。
(太字が追加修正した部分)

#import "SwipesViewController.h"

@implementation SwipesViewController

@synthesize label;
@synthesize gestureStartPoint;

- (void)eraseText {
    label.text = @"";
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad {
    [super viewDidLoad];

    UISwipeGestureRecognizer *vertical = [[[UISwipeGestureRecognizer alloc]
        initWithTarget:self action:@selector(reportVerticalSwipe:)] autorelease];
    vertical.direction = UISwipeGestureRecognizerDirectionUp |
        UISwipeGestureRecognizerDirectionDown;
    [self.view addGestureRecognizer:vertical];

    UISwipeGestureRecognizer *horizontal = [[[UISwipeGestureRecognizer alloc]
        initWithTarget:self action:@selector(reportHorizontalSwipe:)] autorelease];
    horizontal.direction = UISwipeGestureRecognizerDirectionLeft |
        UISwipeGestureRecognizerDirectionRight;
    [self.view addGestureRecognizer:horizontal];

}

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

    self.label = nil;
    [super viewDidUnload];
}

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

#pragma mark -

- (void)reportHorizontalSwipe:(UIGestureRecognizer *)recognizer {
    label.text = @"Horizontal swipe detected";
    [self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}

- (void)reportVerticalSwipe:(UIGestureRecognizer *)recognizer {
    label.text = @"Vertical swipe detected";
    [self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
}


@end

7839


1)viewDidLoadメソッド

viewDidLoadメソッドはビューコントローラのビューを読み込んだ後に呼び出されるメソッドで、ここではGesture Recognizerの生成と初期化を行っています。

Gesture Recognizerの生成と初期化)

最初にUIGestureRecognizerの具象サブクラス(スワイプの場合UISwipeGestureRecognizerクラス)のインスタンスをallocメソッドで生成し、initWithTarget:action:メソッドでGesture Recognizerを初期化します。

アクションメッセージはSwipesViewController.m内に記述するので、第一引数targetはselfに、実行するアクションメソッドを指定する第二引数は、垂直スワイプはreportVerticalSwipe:、水平スワイプはreportHorizontalSwipe:メソッドを指定します。

どちらの初期化コードもautoreleaseメソッドでGesture Recognizerのインスタンスの解放を指定していますが、Apple公式のGesture Recognizerを使用しているサンプルコードを4つ確認した所、内3つは普通にreleaseメソッドで解放しているので、特にautoreleaseメソッドで解放した方が良いという理由は無さそうです。

今回の場合はジェスチャを認識した後、その結果をラベルに2秒間表示するだけなので、addGestureRecognizer:メソッドの呼び出し直後でreleaseしても問題ありません。

スワイプ方向の設定)

垂直スワイプ用のインスタンスverticalと水平スワイプ用のインスタンスhorizontalそれぞれに、directionプロパティで認識するスワイプの方向を設定します。

垂直スワイプには上下方向(UISwipeGestureRecognizerDirectionUpUISwipeGestureRecognizerDirectionDownのOR)、水平スワイプには左右方向(UISwipeGestureRecognizerDirectionLeftUISwipeGestureRecognizerDirectionRightのOR)を指定します。

ビューへのアタッチ)

ビューコントローラSwipesViewController下のビューに、addGestureRecognizer:メソッドで両スワイプのGesture Recognizerをアタッチします。

initWithTarget:action:

- (id)initWithTarget:(id)target action:(SEL)action

割り当てられたGesture Recognizerオブジェクトを、ターゲットとアクションセレクタで初期化します。

戻り値は具象UIGestureRecognizerサブクラスの初期化されたインスタンス、またはオブジェクトの初期化を試みた際にエラーが発生した場合はnilになります。

このメソッドは指定イニシャライザです。

Gesture Recognizerが生成された後、addTarget:action:を呼び出すことによって他のターゲット/アクションのペアを関連付けることができます。

target:ジェスチャを認識した時に、レシーバによって送信されるアクションメッセージを受信するオブジェクトを指定します。
nilは有効な値ではありません。

action:レシーバで認識されたジェスチャを処理する、ターゲットによって実装されたメソッドを識別するセレクタを指定します。
アクションセレクタは、クラスの概要で説明したシグニチャに準拠する必要があります。
NULLは有効な値ではありません。

direction

@property(nonatomic) UISwipeGestureRecognizerDirection direction

このGesture Recognizerでのスワイプの許可される方向です。

デフォルトの方向はUISwipeGestureRecognizerDirectionRightです。

詳細についてはUISwipeGestureRecognizerDirection定数の説明を参照してください。

UISwipeGestureRecognizerDirection

スワイプの方向です。

typedef enum {
    UISwipeGestureRecognizerDirectionRight = 1 << 0,
    UISwipeGestureRecognizerDirectionLeft = 1 << 1,
    UISwipeGestureRecognizerDirectionUp = 1 << 2,
    UISwipeGestureRecognizerDirectionDown = 1 << 3
} UISwipeGestureRecognizerDirection;

UISwipeGestureRecognizerDirectionRight
右方向へのタッチスワイプ。
この方向はデフォルトです。

UISwipeGestureRecognizerDirectionLeft
左方向へのタッチスワイプ。

UISwipeGestureRecognizerDirectionUp
上方向へのタッチスワイプ。

UISwipeGestureRecognizerDirectionDown
下方向へのタッチスワイプ。

addGestureRecognizer:

- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer

ビューにGesture Recognizerをアタッチします。

ビューにGesture Recognizerをアタッチすると、タッチを受信するビューとその全てのサブビューにヒットテストを行い、表されたジェスチャのスコープを定義します。

ビューはGesture Reconizerを保持します。

gestureRecognizerUIGestureRecognizerクラスから派生したクラスのオブジェクトを指定します。
このパラメータはnilでない必要があります。


2)reportHorizontalSwipe:メソッド

reportHorizontalSwipe:メソッドは、水平スワイプが認識された場合に呼び出されるメソッドで、textプロパティでラベルの文字列を設定して表示した後、performSelector:withObject:afterDelay:メソッドで2秒後にeraseTextメソッドを呼び出すことによって文字列を消去しています。


3)reportVerticalSwipe:メソッド

reportVerticalSwipe:メソッドは、垂直スワイプが認識された場合に呼び出されるメソッドで、textプロパティでラベルの文字列を設定して表示した後、performSelector:withObject:afterDelay:メソッドで2秒後にeraseTextメソッドを呼び出すことによって文字列を消去しています。



参考文献

iOSイベント処理ガイド

UIGestureRecognizer Class Reference

UISwipeGestureRecognizer Class Reference

UIView Class Reference

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

UISwipeGestureRecognizerクラス

2012. 02. 28
●概要

UISwipeGestureRecognizerは、一つ以上の方向へのスワイプジェスチャを検出するUIGestureRecognizerの具象サブクラスです。

スワイプは単発のジェスチャであり、したがって関連付けされたアクションメッセージはジェスチャ毎に一回のみ送信されます。

UISwipeGestureRecognizerは、指定されたタッチ数(numberOfTouchesRequired)がスワイプとみなすのに十分許容できる方向(direction)に移動した時にスワイプと認識します。

スワイプは低速または高速にすることができます。

低速スワイプは高い方向精度が必要ですが短距離で済み、高速スワイプは低い方向精度で長距離が必要となります。

UIGestureRecognizerメソッドのlocationInView:locationOfTouch:inView:を呼び出すことによって、スワイプの開始位置を決定することができます。

ジェスチャに複数のタッチが含まれている場合、前者のメソッドは重心を提供し、後者は特定のタッチ位置を提供します。



●タスク

●ジェスチャの構成


direction    property
numberOfTouchesRequired    property



●プロパティ

Objective-Cのプロパティについての詳細は、『The Objective-C Programming Language』の『Properties』を参照してください。

direction


@property(nonatomic) UISwipeGestureRecognizerDirection direction

このGesture Recognizerでのスワイプの許可される方向です。

デフォルトの方向はUISwipeGestureRecognizerDirectionRightです。

詳細についてはUISwipeGestureRecognizerDirection定数の説明を参照してください。


numberOfTouchesRequired


@property(nonatomic) NSUInteger numberOfTouchesRequired

スワイプジェスチャを認識するために必要なタッチ数です。

デフォルト値は1です。



●定数

UISwipeGestureRecognizerDirection

スワイプの方向です。

typedef enum {
    UISwipeGestureRecognizerDirectionRight = 1 << 0,
    UISwipeGestureRecognizerDirectionLeft = 1 << 1,
    UISwipeGestureRecognizerDirectionUp = 1 << 2,
    UISwipeGestureRecognizerDirectionDown = 1 << 3
} UISwipeGestureRecognizerDirection;

UISwipeGestureRecognizerDirectionRight
右方向へのタッチスワイプ。
この方向はデフォルトです。

UISwipeGestureRecognizerDirectionLeft
左方向へのタッチスワイプ。

UISwipeGestureRecognizerDirectionUp
上方向へのタッチスワイプ。

UISwipeGestureRecognizerDirectionDown
下方向へのタッチスワイプ。



参考文献

UISwipeGestureRecognizer Class Reference

UIGestureRecognizerクラス

2012. 02. 27
●概要

UIGestureRecognizerは、具象Gesture Recognizerクラスのための抽象基底クラスです。

Gesture Recognizerオブジェクト(または単にGesture Recognizer)は、ジェスチャを認識するためのロジックと認識上での作用を分離します。

これらのオブジェクトのいずれかが共通のジェスチャを認識する、またはいくつかのケースでは、ジェスチャの変化はそれぞれの指定されたターゲットオブジェクトにアクションメッセージを送信します。

UIGestureRecognizerの具象サブクラスは以下の通りです。

    UITapGestureRecognizer
    UIPinchGestureRecognizer
    UIRotationGestureRecognizer
    UISwipeGestureRecognizer
    UIPanGestureRecognizer
    UILongPressGestureRecognizer

UIGestureRecognizerクラスは、全ての具象Gesture Recognizer用に設定することができる共通動作の設定を定義します。

また、そのデリゲート(UIGestureRecognizerDelegateプロトコルを採用したオブジェクト)と通信することができ、それによって精細ないくつかの動作のカスタマイズが可能になります。

Gesture Recognizerでは、ヒットテストされた特定のビューとそのビューの全てのサブビューでタッチを処理します。

したがってビューは関連付けされる必要があります。

関連付けを行うには、UIViewのメソッドaddGestureRecognizer:を呼び出す必要があります。

Gesture Recognizerはビューのレスポンダチェーンに参加しません。

Gesture Recognizerは、関連付けられた一つ以上のターゲット/アクションのペアを持っています。

複数のターゲット/アクションのペアがある場合、それらは別個であり、累積されません。

ジェスチャの認識結果は、それらのペアのそれぞれのターゲットにアクションメッセージがディスパッチされます。

呼び出されるアクションメソッドは、以下のシグニチャのいずれかに準拠する必要があります。

- (void)handleGesture;
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;

後者のシグニチャに準拠したメソッドは、いくつかのケースではターゲットが追加情報を照会するためにGesture Recognizerにメッセージを送信することができます。

例えばターゲットは、このジェスチャのアクションメソッドの最後の呼び出し以降の回転角度(ラジアン単位)について、UIRotationGestureRecognizerオブジェクトに尋ねることができます。

Gesture Recognizerのクライアントも、locationInView:またはlocationOfTouch:inView:を呼び出すことによって、ジェスチャの場所について尋ねることができます。

Gesture Recognizerによって解釈されたジェスチャは、単発または連続のどちらかになります。

単発ジェスチャは、ダブルタップなどのマルチタッチシーケンスで一度だけ発生し、単一のアクションの結果が送信されます。

ただし、Gesture Recognizerが回転ジェスチャなど連続ジェスチャと解釈する場合、マルチタッチシーケンスの終了まで各増分変化のアクションメッセージが送信されます。

タッチイベントは、Gesture Recognizerにアタッチされたヒットテストビューに提供する前に、ウィンドウに提供されます。

通常、Gesture Recognizerがマルチタッチシーケンス内のタッチのストリームを解析し、そのジェスチャを認識できなかった場合、ビューがタッチの完全な補完を受信します。

Gesture Recognizerがジェスチャを認識できた場合、ビューは残りのタッチをキャンセルします。

通常のジェスチャ認識のアクションのシーケンスは、cancelsTouchesInViewdelaysTouchesBegandelaysTouchesEndedプロパティのデフォルト値によって、以下のように経路が決定されます。

  • cancelsTouchesInView
    Gesture Recognizerがジェスチャを認識できた場合、ジェスチャの残りのタッチはビューからアンバインドされます(つまりウィンドウはそれらを提供しません)。
    ウィンドウは以前に提供されたタッチは(touchesCancelled:withEvent:)と共にキャンセルします。
    Gesture Recognizerがジェスチャを認識できなかった場合、ビューはマルチタッチシーケンス内の全てのタッチを受信します。

  • delaysTouchesBegan
    Gesture Recognizerがタッチイベントを解析する時、ジェスチャの認識に失敗していない場合は、ウィンドウはUITouchPhaseBeganフェーズでのアタッチされたビューへのタッチオブジェクトの提供を保留します。
    Gesture Recognizerが後続のジェスチャを認識した場合、ビューはタッチオブジェクトを受信しません。
    Gesture Recognizerがジェスチャを認識しない場合、ウィンドウはビューのtouchesBegan:withEvent:メソッドを呼び出して(その後おそらくタッチの現在位置の通知にtouchesMoved:withEvent:の呼び出して)これらのオブジェクトを提供します。

  • delaysTouchesEnded
    Gesture Recognizerがタッチイベントを解析する時、ジェスチャの認識に失敗していない場合は、ウィンドウはUITouchPhaseEndedでのアタッチされたビューへのタッチオブジェクトの提供を保留します。
    Gesture Recognizerが後続のジェスチャを認識した場合、タッチは(touchesCancelled:withEvent:メッセージで)キャンセルされます。
    Gesture Recognizerがジェスチャを認識しない場合、ウィンドウはビューのtouchesEnded:withEvent:メソッドを呼び出してこれらのオブジェクトを提供します。

上記での『認識』は、必ずしも認識された状態への遷移と同一ではないことに注意してください。


使用の特別な考慮事項

実行時に与えられたiOSのリリースでクラスを使用できるかを決定するには、通常クラスがnilかどうかをチェックします。

残念ながら、このテストはUIGestureRecognizerでは正確ではありません。

このクラスはiOS 3.2で公開を開始しましたが、それまでの開発期間は短いものでした。

クラスはそれ以前のリリースから存在していますが、それ以前のリリースではこのクラスと他のGesture Recognizerクラスの使用をサポートしていません。

それらのクラスのインスタンスの使用を試みるべきではありません。

アプリケーションでGesture Recognizerを使用できるかどうかを実行時に決定するには、クラスが存在するかどうかのテストと、インスタンスを割り当ててセレクタlocationInView:に応答するかを確認してください。

このメソッドはiOS 3.2までクラスに追加されていませんでした。

コードは次のようになります。

UIGestureRecognizer *gestureRecognizer = [[UIGestureRecognizer alloc] initWithTarget:self action:@selector(myAction:)];

if (![gestureRecognizer respondsToSelector:@selector(locationInView:)]) {
    [gestureRecognizer release];
    gestureRecognizer = nil;
}
// do something else if gestureRecognizer is nil


サブクラス化の注意

UIGestureRecognizerのサブクラスを生成することで、『チェックマーク』ジェスチャなどの独自のジェスチャを認識することができます。

このように具体的なGesture Recognizerを生成する場合は、UIGestureRecognizerSubclass.hヘッダファイルをインポートしてください。

このヘッダは、サブクラスがオーバーライド、呼び出し、またはリセットするために必要な全てのメソッドとプロパティを宣言しています。

Gesture Recognizerは定義済の状態マシン内で動作し、マルチタッチイベントを処理すると後続の状態に遷移します。

状態と可能な遷移は連続的か単発かで異なります。

全てのGesture RecognizerはPossible状態(UIGestureRecognizerStatePossible)でマルチタッチシーケンスを開始します。

単発ジェスチャは、ジェスチャの解釈の成否に応じて、PossibleからRecognized(UIGestureRecognizerStateRecognized)またはFailed(UIGestureRecognizerStateFailed)のいずれかに遷移します。

Gesture RecognizerがRecognizedに遷移した場合、ターゲットにアクションメッセージを送信します。

連続ジェスチャでは、Gesture Recognizerが作る可能性のある状態遷移は、次に示すようにより多数から成ります。

    Possible ----> Began ----> [Changed] ----> Cancelled
    Possible ----> Began ----> [Changed] ----> Ended

変更された状態は任意であり、CancelledまたはEnded状態に到達する前に複数回発生する可能性があります。

Gesture Recognizerは各状態遷移時にアクションメッセージを送信します。

したがってピンチなどの連続ジェスチャでは、アクションメッセージは2本の指が互いに向かって、あるいは離れて移動するものとして送信されます。

これらの状態を表す列挙型定数は、型UIGestureRecognizerStateです。
(RecognizedとEnded状態の定数は同義であることに注意してください)

サブクラスは状態が遷移する間に、適切な値にstateプロパティを設定する必要があります。


メソッドのオーバーライド

サブクラスがオーバーライドする必要があるメソッドは『サブクラス用のメソッド』で説明しています。

これらは(上述したように)定期的にstateプロパティをリセットする必要があり、ignoreTouch:forEvent:メソッドを呼び出すことができます。


特別な考慮事項

stateプロパティは読み込み専用としてUIGestureRecognizer.hで宣言されています。

このプロパティの宣言はGesture Recognizerのクライアントを対象としています。

UIGestureRecognizerのサブクラスは、UIGestureRecognizerSubclass.hをインポートする必要があります。

このヘッダファイルには読み書き可能なstateの再宣言が含まれています。



●タスク

●Gesture Recognizerの初期化


– initWithTarget:action:

●ターゲットとアクションの追加と削除


– addTarget:action:
– removeTarget:action:

●ジェスチャのタッチと場所の取得


– locationInView:
– locationOfTouch:inView:
– numberOfTouches

●Gesture Recognizerの状態とビューの取得


  state    property
  view    property
  enabled    property

●タッチのキャンセルと遅延


  cancelsTouchesInView    property
  delaysTouchesBegan    property
  delaysTouchesEnded    property

●Gesture Recognizer間の依存関係の指定


– requireGestureRecognizerToFail:

●デリゲートの設定と取得


  delegate    property

サブクラス用のメソッド


UIGestureRecognizerSubclass.hヘッダファイルには、UIGestureRecognizerのサブクラスによってのみ呼び出しまたはオーバーライドされることを意図されたメソッドを宣言する、クラスの拡張が含まれています。

単にUIGestureRecognizerの具象サブクラスを使用するクライアントでは、(指摘したものを除いて)これらのメソッドを呼び出さないでください。

– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
– reset
– ignoreTouch:forEvent:
– canBePreventedByGestureRecognizer:
– canPreventGestureRecognizer:



●プロパティ

Objective-Cのプロパティについての詳細は、『The Objective-C Programming Language』の『Properties』を参照してください。

state

@property(nonatomic, readonly) UIGestureRecognizerState state

Gesture Recognizerの現在の状態です。(読み込みのみ)

Gesture Recognizerの可能な状態はUIGestureRecognizerState型の定数によって表すことができます。

これらの状態のいくつかは単発ジェスチャに適用されません。

stateプロパティの読み込みのみのバージョンは、サブクラスではなく、Gesture Recognizerクラスのクライアントのために意図されています。

特別な考慮事項

UIGestureRecognizerのサブクラスはstateプロパティの読み書きバージョンを使用する必要があります。

サブクラスはUIGestureRecognizerSubclass.hヘッダファイルをインポートする時に、再宣言を取得します。

@property(nonatomic,readwrite) UIGestureRecognizerState state;

単発ジェスチャのGesture Recognizerは、UIGestureRecognizerStatePossibleからUIGestureRecognizerStateFailedまたはUIGestureRecognizerStateRecognizedへ遷移します。

連続ジェスチャのGesture RecognizerはUIGestureRecognizerStatePossibleからUIGestureRecognizerStateBeganUIGestureRecognizerStateChangedUIGestureRecognizerStateEndedという順のフェーズで遷移します。

ただしキャンセルのタッチを受信した場合は、UIGestureRecognizerStateCancelledに遷移します。

連続ジェスチャのGesture Recognizerがジェスチャとしてマルチタッチシーケンスを解釈できなかった場合、UIGestureRecognizerStateFailedへ遷移します。



●インスタンスメソッド

initWithTarget:action:

- (id)initWithTarget:(id)target action:(SEL)action

割り当てられたGesture Recognizerオブジェクトを、ターゲットとアクションセレクタで初期化します。

戻り値は具象UIGestureRecognizerサブクラスの初期化されたインスタンス、またはオブジェクトの初期化を試みた際にエラーが発生した場合はnilになります。

このメソッドは指定イニシャライザです。

Gesture Recognizerが生成された後、addTarget:action:を呼び出すことによって他のターゲット/アクションのペアを関連付けることができます。

target
:ジェスチャを認識した時に、レシーバによって送信されるアクションメッセージを受信するオブジェクトを指定します。
nilは有効な値ではありません。

action
:レシーバで認識されたジェスチャを処理する、ターゲットによって実装されたメソッドを識別するセレクタを指定します。
アクションセレクタは、クラスの概要で説明したシグニチャに準拠する必要があります。
NULLは有効な値ではありません。


numberOfTouches

- (NSUInteger)numberOfTouches

レシーバによって表されるジェスチャに関するタッチ数を返します。

戻り値は、レシーバによってプライベートな配列内に保持される、UITouchオブジェクト数です。

これらの各オブジェクトは、現在のジェスチャのタッチを表しています。

ループ内でこのメソッドによって返される値を使用して、locationOfTouch:inView:メソッドを使用して個々のタッチ位置を尋ねることができます。


requireGestureRecognizerToFail:

- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer

レシーバと他のGesture Recognizer間の依存関係を生成します。

このメソッドは、レシーバのUIGestureRecognizerStatePossibleからの遷移を遅延させる他のGesture Recognizerとの関係を生成します。

レシーバの遷移状態はotherGestureRecognizerで発生するものに依存します。
例えば、ダブルタップジェスチャが失敗した際にシングルタップジェスチャが必要な場合に、このメソッドを呼び出すことができます。

otherGestureRecognizer
:他のGesture Recognizerオブジェクト(UIGestureRecognizerのサブクラスのインスタンス)を指定します。



●定数

UIGestureRecognizerState

Gesture Recognizerの現在の状態です。

typedef enum {
    UIGestureRecognizerStatePossible,

    UIGestureRecognizerStateBegan,
    UIGestureRecognizerStateChanged,
    UIGestureRecognizerStateEnded,
    UIGestureRecognizerStateCancelled,

    UIGestureRecognizerStateFailed,

    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
} UIGestureRecognizerState;

UIGestureRecognizerStatePossible
Gesture Recognizerはまだジェスチャを認識していないが、タッチイベントを評価することができます。
これはデフォルト状態です。

UIGestureRecognizerStateBegan
Gesture Recognizerは連続ジェスチャとして認識されたタッチオブジェクトを受信しています。
これは実行ループの次のサイクルでアクションメッセージ(またはメッセージ)を送信します。

UIGestureRecognizerStateChanged
Gesture Recognizerは連続ジェスチャへの変更として認識されたタッチを受信しています。
これは実行ループの次のサイクルでアクションメッセージ(またはメッセージ)を送信します。

UIGestureRecognizerStateEnded
Gesture Recognizerは連続ジェスチャの終了として認識されたタッチを受信しています。
これは実行ループの次のサイクルでアクションメッセージ(またはメッセージ)を送信し、UIGestureRecognizerStatePossibleへ状態をリセットします。

UIGestureRecognizerStateCancelled
Gesture Recognizerは連続ジェスチャをキャンセルするタッチを受信しています。
これは実行ループの次のサイクルでアクションメッセージ(またはメッセージ)を送信し、UIGestureRecognizerStatePossibleへ状態をリセットします。

UIGestureRecognizerStateFailed
Gesture Recognizerはジェスチャとして認識することができなかったマルチタッチシーケンスを受信しています。
アクションメッセージは送信されず、Gesture RecognizerはUIGestureRecognizerStatePossibleにリセットされます。

UIGestureRecognizerStateRecognized
Gesture Recognizerはジェスチャとして認識されたマルチタッチシーケンスを受信しています。
これは実行ループの次のサイクルでアクションメッセージ(またはメッセージ)を送信し、UIGestureRecognizerStatePossibleへ状態をリセットします。

Gesture Recognizerはタップやスワイプなどの単発イベントを認識しますが、ジェスチャ内の変更を報告しません。

言い換えると、単発ジェスチャはBeganとChanged状態を遷移させず、失敗やキャンセルをすることができません。



参考文献

UIGestureRecognizer Class Reference

Swipes(1)~イベント処理メソッド

2012. 02. 26
Swipesは一本指での水平/垂直スワイプ動作を検出してラベルで表示します。

7824


●プロジェクトの作成

View-based Applicationテンプレートで、プロジェクト名を『Swipes』とします。

7815

7825


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

指の移動をスワイプとする移動距離と許容変位、水平/垂直スワイプと認識されたことを表示するラベルのアウトレットと、スワイプの開始座標を保持するプロパティ、そして(表示から一定時間後に)ラベルを消去するメソッドの宣言を行います。
(太字が追加した部分)

#define kMinimumGestureLength 25
#define kMaximumVariance 5


#import <UIKit/UIKit.h>

@interface SwipesViewController : UIViewController {
    UILabel *label;
    CGPoint gestureStartPoint;

}

@property (nonatomic, retain) IBOutlet UILabel *label;
@property CGPoint gestureStartPoint;

- (void)eraseText;


@end

7826


1)定数

2つの定数はジェスチャ(指の動き)をスワイプとして認識するために必要な値を指定しています。

kMinimumuGestureLengthはスワイプと認識するために必要な移動距離を示すもので、ここでは25ピクセルに設定します。

kMaximumVarianceはスワイプと認識するために必要な許容変位を示すもので、ここでは5ピクセルに設定します。

つまりタッチされた始点から水平(垂直)方向に25ピクセル移動し、且つその誤差が垂直(水平)方向に5ピクセル以内であった場合にスワイプと認識されることになります。


2)アウトレット

ラベルlabelはジェスチャが水平または垂直のスワイプと認識された場合に表示するアウトレットです。


3)変数

変数gestureStartPointはジェスチャをスワイプと認識するために必要な始点座標を保持します。


4)メソッド

eraseTextメソッドは(表示から一定時間後に)ラベルを消去するメソッドです。


●SwipesViewController.xibの編集

SwipesViewController.hヘッダファイルの変更が終わったら保存し、ビューコントローラのnibファイルSwipesViewController.xibを開いてラベルの追加を行います。


1)ラベルの追加

LibraryウィンドウのLabelを選択し、Viewウィンドウの上方に1つラベルを設置します。

7827


2)ラベルへのアウトレットの接続

DocumentウィンドウでFile's Ownerを選択し、InspectorウィンドウでConnectionsタブを開き、OutletsのlabelをViewウィンドウのラベルに接続します。

7828


3)ラベルのサイズ変更

Documentウィンドウでラベルを選択し、InspectorウィンドウでSizeタブを開き、X:20、Y:20、W:280、H:21にサイズを変更します。


4)ラベルの属性変更

InspectorウィンドウのAttributesタブを開き、属性の変更を行います。

Label)

・Text
予め入っている文字列『Label』を削除します。
これにより初期状態では何も表示されないことになります。

・Layout:Alignment
文字列のレイアウトを左寄せから中央揃えに変更します。

7829


5)ビューの属性変更

DocumentウィンドウでViewを選択し、InspectorウィンドウでAttributesタブを開き、ViewのInteractionの『Multiple Touch』にチェックを入れます。
(今回に限っては一本指の動作検出なので実際には不要ですが、後に改変して複数指に対応するためにチェックを入れておきます)

この項目はUIViewクラスのmultipleTouchEnabledプロパティに相当します。

7830

全ての変更が済んだらnibファイルを保存します。


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

ヘッダファイルで宣言したプロパティの実装と、ラベルを消去するeraseTextメソッド、および2つのタッチイベントメソッドを追加します。
(太字が追加した部分)

#import "SwipesViewController.h"

@implementation SwipesViewController

@synthesize label;
@synthesize gestureStartPoint;

- (void)eraseText {
    label.text = @"";
}


/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
}
*/


// Override to allow orientations other than the default portrait orientation.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

    self.label = nil;
    [super viewDidUnload];

}

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

#pragma mark -

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    gestureStartPoint = [touch locationInView:self.view];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint currentPosition = [touch locationInView:self.view];

    CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
    CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);

    if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
        label.text = @"Horizontal swipe detected";
        [self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
    }
    else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
        label.text = @"Vertical swipe detected";
        [self performSelector:@selector(eraseText) withObject:nil afterDelay:2];
    }
}


@end

7835


1)プロパティの実装

ヘッダファイルで宣言した2つのプロパティlabelとgestureStartPointを@synthesizeで実装します。


2)eraseTextメソッド

eraseTextメソッドはラベルのテキストを消去するメソッドで、タッチ移動を検出するtouchesMoved:withEvent:メソッドから呼び出されます。

ここではtextプロパティでラベルの文字列を空に設定しているだけで、『表示から一定時間後に』という部分は後述するtouchesMoved:withEvent:メソッド側で行っています。


3)不要なメソッドの削除

テンプレートで自動生成されるメソッドの内、initWithNibName:bundle:loadViewメソッドは今回使用しないので削除します。

viewDidLoadメソッドは後で利用するので、コメントアウトのまま残しておきます。



4)shouldAutorotateToInterfaceOrientation:メソッド

shouldAutorotateToInterfaceOrientation:のコメントアウトを解除してデフォルトのまま実装しています。


5)viewDidUnloadメソッド


viewDidUnloadにObjective-Cオブジェクトのプロパティlabelの所有権放棄を追加しています。


6)deallocメソッド

deallocにObjective-Cオブジェクトのプロパティlabelの解放を追加しています。


7)touchesBegan:withEvent:メソッド

touchesBegan:withEvent:メソッドはビューに一本以上の指が触れた時に呼び出されるメソッドで、ここでは始点座標の保持を行っています。

タッチオブジェクトのセットからanyObjectメソッドで適切なオブジェクトを選抜し、そのオブジェクトからlocationInView:メソッドでビューにおける始点座標を取得します。


8)touchesMoved:withEvent:メソッド

touchesMoved:withEvent:メソッドはビューで一本以上の指が移動した時に呼び出されるメソッドで、ここでは移動中の現在の座標を始点座標と比較し、予め定数で指定した範囲のジェスチャであればスワイプとして認識し、ラベルを表示を行います。

まずタッチオブジェクトのセットからanyObjectメソッドで適切なオブジェクトを選抜し、そのオブジェクトからlocationInView:メソッドでビューにおける現在の座標を取得します。

次にx座標とy座標それぞれについて、始点座標と現在の座標との差の絶対値を取ります。

x座標の差分deltaXが認識距離(kMinimumGestureLength)以上で、且つy座標の差分deltaYが許容変位(kMaximumVariance)以内であれば水平スワイプとして認識します。

y座標の差分deltaYが認識距離以上で、且つx座標の差分deltaXが許容変位以内であれば垂直スワイプとして認識します。

認識されたスワイプの種類に応じて、textプロパティでラベルの文字列を設定して表示した後、performSelector:withObject:afterDelay:メソッドで2秒後にeraseTextメソッドを呼び出すことによって文字列を消去しています。



参考文献

iOSイベント処理ガイド

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

TouchExplorer

2012. 02. 25
QuartzFunGLFunでは、ビューへのタッチとドラッグを検出し、それに合わせて図形を描画していましたが、このようなタッチイベントの取得と応答について掘り下げてみます。

TouchExplorerはビューへのタッチイベントを取得し、現在のタッチ状況とタップ回数、そしてタッチポイント数(何本の指がタッチされているか)をラベルで表示します。

7814


●プロジェクトの作成

View-based Applicationテンプレートで、プロジェクト名を『TouchExplorer』とします。

7815

7816


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

現在のタッチ状況とタップ回数、そしてタッチポイント数を表示する3つラベルのアウトレットと、タッチイベントの発生に応じてラベルを更新するメソッドの宣言を行います。
(太字が追加した部分)

#import <UIKit/UIKit.h>

@interface TouchExplorerViewController : UIViewController {
    UILabel *messageLabel;
    UILabel *tapsLabel;
    UILabel *touchesLabel;

}

@property (nonatomic, retain) IBOutlet UILabel *messageLabel;
@property (nonatomic, retain) IBOutlet UILabel *tapsLabel;
@property (nonatomic, retain) IBOutlet UILabel *touchesLabel;

- (void)updateLabelsFromTouches:(NSSet *)touches;


@end

7817

ラベルは画面での表示順と同じく、上から
・messageLabel:タッチ状況
・tapsLabel:タップ数
・touchesLabel:タッチポイント数
を示します。

updateLabelsFromTouches:メソッドは、タッチイベントの発生に応じてラベルに表示する文字列を更新するメソッドです。


●TouchExplorerViewController.xibの編集

TouchExplorerViewController.hヘッダファイルの変更が終わったら保存し、ビューコントローラのnibファイルTouchExplorerViewController.xibを開いてラベルの追加を行います。


1)ラベルの追加

LibraryウィンドウのLabelを選択し、Viewウィンドウの上方に3つラベルを設置します。

7818


2)ラベルへのアウトレットの接続

DocumentウィンドウでFile's Ownerを選択し、InspectorウィンドウでConnectionsタブを開き、OutletsのmessageLabel、tapsLabel、touchesLabelをViewウィンドウのラベルに上から順に接続します。

7819


3)ラベルのサイズ変更

Documentウィンドウで各ラベルを選択し、InspectorウィンドウでSizeタブを開き、下表のようにサイズを変更します。

LabelXYWH
messageLabel202028021
tapsLabel204928021
touchesLabel207828021


4)ラベルの属性変更

Documentウィンドウで各ラベルを選択し、InspectorウィンドウでAttributesタブを開き、属性の変更を行います。

Label)

・Text
予め入っている文字列『Label』を削除します。
これにより初期状態では何も表示されないことになります。
この項目はUILabelクラスのtextプロパティに相当します。

・Layout:Alignment
文字列のレイアウトを左寄せから中央揃えに変更します。
この項目はUILabelクラスのtextAlignmentプロパティに相当します。

・Highlighted
ラベルを強調表示にする際の色の設定です。
原著であるApress社のiOS 3.x版のサンプルコードでは『Default』になっていますが、iOS 4.3のテンプレートではiOS 4.x版のサンプルコードと同じく『White Color』になっています。
(使用しないので特に変更はしません)
この項目はUILabelクラスのhighlightedTextColorプロパティに相当します。

View)

・Mode
ビューのサイズが変更された時に画像をどのように配置するかの設定です。
原著であるApress社のiOS 3.x版のサンプルコードでは『Scale To Fill』になっていますが、iOS 4.3のテンプレートではiOS 4.x版のサンプルコードと同じく『Left』になっています。
(画像レイアウトの設定なので、特に変更はしません)
この項目はUIViewクラスのcontentModeプロパティに相当します。

7820


5)ビューの属性変更

DocumentウィンドウでViewを選択し、InspectorウィンドウでAttributesタブを開き、ViewのInteractionの『Multiple Touch』にチェックを入れます。

この項目はUIViewクラスのmultipleTouchEnabledプロパティに相当します。

7821

全ての変更が済んだらnibファイルを保存します。


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

ヘッダファイルで宣言した3つのプロパティの実装と、ラベルを更新するupdateLabelsFromTouches:メソッド、および4つのタッチイベントメソッドを追加します。
(太字が追加した部分)

#import "TouchExplorerViewController.h"

@implementation TouchExplorerViewController

@synthesize messageLabel;
@synthesize tapsLabel;
@synthesize touchesLabel;

- (void)updateLabelsFromTouches:(NSSet *)touches {
    NSUInteger numTaps = [[touches anyObject] tapCount];
    NSString *tapsMessage = [[NSString alloc] initWithFormat:@"%d taps detected", numTaps];
    tapsLabel.text = tapsMessage;
    [tapsMessage release];

    NSUInteger numTouches = [touches count];
    NSString *touchMsg = [[NSString alloc] initWithFormat:@"%d touches detected", numTouches];
    touchesLabel.text = touchMsg;
    [touchMsg release];
}


// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

    self.messageLabel = nil;
    self.tapsLabel = nil;
    self.touchesLabel = nil;

    [super viewDidUnload];
}

- (void)dealloc {
    [messageLabel release];
    [tapsLabel release];
    [touchesLabel release];

    [super dealloc];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    messageLabel.text = @"Touches Began";
    [self updateLabelsFromTouches:touches];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    messageLabel.text = @"Touches Cancelled";
    [self updateLabelsFromTouches:touches];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    messageLabel.text = @"Touches Stopped.";
    [self updateLabelsFromTouches:touches];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    messageLabel.text = @"Drag Detected";
    [self updateLabelsFromTouches:touches];
}


@end

7822


1)プロパティの実装

ヘッダファイルで宣言した3つのプロパティmessageLabel、tapsLabel、touchesLabelを@synthesizeで実装します。


2)updateLabelsFromTouches:メソッド

updateLabelsFromTouches:メソッドはラベルのテキストを更新するメソッドで、タッチイベントが発生する毎に呼び出されます。

ここでは3つのラベルの内、タップ回数を示すtapsLabelとタッチポイント数を示すtouchesLabelの更新を行っています。
(タッチ状況を示すmessageLabelは、呼び出し元であるタッチイベントメソッドで設定されます)

タップ回数)

まず、タッチオブジェクトのセットからanyObjectメソッドで適切なオブジェクトを選抜し、そのオブジェクトからtapCountプロパティでタップ回数を取得します。

そしてinitWithFormat:メソッドでタップ回数を含む文字列を生成し、textプロパティでラベルの文字列に設定します。

タッチポイント数)

タッチポイント数はタッチオブジェクトのセットの要素数で表されているので、要素数をcountメソッドで取得します。

以降はタップ回数と同様にinitWithFormat:メソッドでタッチポイント数を含む文字列を生成し、textプロパティでラベルの文字列に設定します。


3)不要なメソッドの削除

テンプレートで自動生成されるメソッドの内、initWithNibName:bundle:loadViewviewDidLoadメソッドは今回使用しないので削除します。


4)shouldAutorotateToInterfaceOrientation:メソッド

shouldAutorotateToInterfaceOrientation:のコメントアウトを解除してデフォルトのまま実装しています。


5)viewDidUnloadメソッド


viewDidUnloadに3つのプロパティの所有権放棄を追加しています。


6)deallocメソッド


deallocに3つのプロパティの解放を追加しています。


7)4つのタッチイベントメソッド

タッチイベントに応答するメソッドは以下の4つです。

touchesBegan:withEvent::ビューに一本以上の指が触れた時に通知
touchesCancelled:withEvent::システムがタッチイベントをキャンセルした時に通知
touchesEnded:withEvent::ビューから指が一本以上離れた時に通知
touchesMoved:withEvent::ビューで一本以上の指が移動した時に通知

各タッチイベントメソッドでは、その内容に応じた状況の文字列をtextプロパティでタッチ状況を示すラベルに設定し、残り2つのラベルを更新するためupdateLabelsFromTouches:メソッドを呼び出します。



参考文献

iOSイベント処理ガイド

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(32)~OpenGL ESテンプレートの利用(7)

2012. 02. 24
Texture2Dクラスは、画像やテキストからOpenGL ESの2Dテクスチャを生成するクラスです。
(『GLFun(12)~Texture2Dクラス(1)』参照)

このクラスはOpenGL ES 1.1のみの対応なので、OpenGL ES 2.0でも使用できるように(今回のGLFunプロジェクトの移植に関する)必要最低限の修正を行います。


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

OpenGL ES 2.0のgl.hのインポートと、3つの初期化メソッド(initWithData:pixelFormat:pixelsWide:pixelsHigh:contentSize:、initWithImage:、initWithString:dimensions:alignment:fontName:fontSize:)にOpenGL ES 2.0からの呼び出しかどうかを判別する引数『version:(BOOL)useOpenGLES2』を追加します。
(太字が追加した部分)

#import <UIKit/UIKit.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES2/gl.h>

(中略)

- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size version:(BOOL)useOpenGLES2;

(中略)

@interface Texture2D (Image)
- (id) initWithImage:(UIImage *)uiImage version:(BOOL)useOpenGLES2;
@end

(中略)

@interface Texture2D (Text)
- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size version:(BOOL)useOpenGLES2;
@end

7807

実際にOpenGL ES 1.1と2.0で判別した処理を行うのはinitWithData:~メソッドだけなのですが、ビューコントローラの描画メソッドから呼び出すのはinitWithImage:メソッドであり、そのinitWithImage:とinitWithString:~メソッドが内部でinitWithData:~メソッドを呼び出しているため、3つとも引数を追加します。


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

OpenGL ES 2.0のglext.hのインポートと、initializeメソッドの修正、および3つの初期化メソッド(initWithData:pixelFormat:pixelsWide:pixelsHigh:contentSize:、initWithImage:、initWithString:dimensions:alignment:fontName:fontSize:)にOpenGL ES 2.0からの呼び出しかどうかを判別する引数『version:(BOOL)useOpenGLES2』を追加します。
(太字が追加した部分)


1)initializeメソッドの修正

Texture2Dクラスにはイニシャライザのinitializeメソッドが有り、インスタンス作成時に暗黙的に呼び出されます。
(『GLFun(14)~Texture2Dクラス(3)』参照)

問題はinitializeメソッド内でOpenGL ES 1.1のみに存在する関数(glTexEnvglEnableClientState)を呼び出している点で、これによりOpenGL ES 2.0からTexture2Dクラスのインスタンスを作成しようとすると『EXC_BAD_ACCESS』が発生することになります。

この問題を回避するため、今回はinitializeメソッドの内容をinitWithData:~メソッドに移植し、initWithData:~メソッド内でOpenGL 1.1の場合は実行、2.0の場合は無視するようにします。

したがって、initializeメソッドの中身は空になります。

+ (void) initialize {
    // These calls need to get called once for the class to work, but if they are called before OpenGL knows about any textures, it crashes,
}


2)initWithData:pixelFormat:pixelsWide:pixelsHigh:contentSize:メソッドの修正

上記の理由により、initializeメソッドの内容をOpenGL ES 1.1のみで実行するよう追加を行います。
(太字が追加修正した部分)

- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size version:(BOOL)useOpenGLES2
{
    GLint saveName;
    if((self = [super init])) {
        if (!useOpenGLES2) {
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        }


(中略)

    }
    return self;
}

7808


3)initWithImage:メソッドの修正

initWithImage:メソッドは内部でinitWithData:~メソッドを呼び出しているので、対応できるよう引数を追加していますが、引数を渡している以外は何もしていません。
(太字が追加した部分)

- (id) initWithImage:(UIImage *)uiImage version:(BOOL)useOpenGLES2
{

(中略)

    self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:width pixelsHigh:height contentSize:imageSize version:useOpenGLES2];

    CGContextRelease(context);
    free(data);

    return self;
}

7809


4)initWithString:dimensions:alignment:fontName:fontSize:メソッドの修正

initWithImage:メソッドと同様です。
(太字が追加した部分)

- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size version:(BOOL)useOpenGLES2
{

(中略)

    self = [self initWithData:data pixelFormat:kTexture2DPixelFormat_A8 pixelsWide:width pixelsHigh:height contentSize:dimensions version:useOpenGLES2];

    CGContextRelease(context);
    free(data);

    return self;
}

7810


●Shader.vsh頂点シェーダプログラムの編集

頂点シェーダプログラムでは、4つの変数宣言の追加と、頂点座標値への正射影行列の適用、そしてテクスチャ座標の受け渡しの追加を行っています。
(太字が追加修正した部分)

attribute vec4 position;
attribute vec2 texcoord;

varying vec4 colorVarying;
varying vec2 texcoordVarying;

uniform vec4 constColor;
uniform mat4 projectionMatrix;


void main()
{
    gl_Position = projectionMatrix * position;

    colorVarying = constColor;
    texcoordVarying = texcoord;
}

7811

テンプレートにある描画色のattribute値colorと、アニメーション用の変換座標のuniform値translateは不要なので削除します。

宣言に追加した4つの変数は以下の通りです。
・テクスチャ座標:texcoord
・フラグメントシェーダに渡すテクスチャ座標:texcoordVarying
・プリミティブの描画色:constColor
・正射影行列:projectionMatrix

gl_Positionでの頂点座標の指定に、正射影行列projectionMatrixを適用します。

プログラムにあるアニメーション用のy座標補正は不要なので削除します。

フラグメントシェーダに渡す描画色は、attribute値colorからuniform値constColorに変更します。

また、フラグメントシェーダへ渡すテクスチャ座標の設定を追加します。


●Shader.fshフラグメントシェーダプログラムの編集

フラグメントシェーダプログラムでは、3つの変数宣言と、テクスチャ使用フラグによるフラグメント色の分岐指定、そしてテクスチャ参照関数によるテクスチャの適用を追加しています。
(太字が追加した部分)

varying lowp vec4 colorVarying;
varying lowp vec2 texcoordVarying;

uniform sampler2D texture;
uniform bool flag;


void main()
{
    if (!flag)
        gl_FragColor = colorVarying;
    else
        gl_FragColor = texture2D(texture, texcoordVarying.st);

}

7812

宣言に追加した3つの変数は以下の通りです。
・頂点シェーダから渡されたテクスチャ座標:texcoordVarying
・2Dテクスチャへのハンドル:texture
・テクスチャ使用を示すフラグ:flag

フラグメント色は、3種の図形の場合は単一の描画色のみ、画像の場合はテクスチャの色のみと排他的に塗りつぶすので、テクスチャ使用のフラグによって分岐しています。

テクスチャ使用の場合は、テクスチャ参照関数texture2Dでテクスチャを貼り付けます。
(『iPhone/iPadゲーム開発のゲンバから…/OpenGL ES 2.0 ④』、『GLSL~8.7 テクスチャ参照関数』、『GLSL~4.1 基本の型』、『GLSL~5.5 ベクトル要素』参照)



参考文献

iPhone/iPadゲーム開発のゲンバから…/OpenGL ES 2.0 ④

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(31)~OpenGL ESテンプレートの利用(6)

2012. 02. 23
OpenGL ES 2.0用のコード部分)

・正射影行列用配列の設定(OpenGL ES 2.0用)

正射影行列用配列proj[ ]の生成には、Apple公式のサンプルコード『GLES2Sample』から流用した変換行列のクラスmatrixにあるmat4f_LoadOrtho関数を使用します。

mat4f_LoadOrthoはOpenGL ES 1.1のglOrthoと同じくビューボリュームの値(left、right、bottom、top、near、far)を指定することで、4×4の正射影行列用配列を作成します。

作成するビューボリュームもオリジナルのGLFunと同じく、左下(0, 0)からビューの(幅, 高さ)の大きさです。

・uniform値の更新の追加(OpenGL ES 2.0用)

glUniformで、描画色と正射影行列、そしてテクスチャの使用フラグのuniform値の設定を行っています。

描画色(UNIFORM_COLOR)は、CGColorGetComponentsで取得した配列componentsをそのまま渡しています。

正射影行列(UNIFORM_PROJECTION_MATRIX)は、上述のmatrixクラス関数で作成したproj[ ]を指定します。

テクスチャ使用のフラグ(UNIFORM_TEXTURE_FLAG)は、デフォルトとして0に設定し、選択図形がImageの時に1に設定されるようにします。

・attribute値の更新の修正(OpenGL ES 2.0用)

テンプレートでは、glVertexAttribPointerによる頂点座標配列(ATTRIB_VERTEX)と頂点色配列(ATTRIB_VERTEX)のポインタの設定と、glEnableVertexAttribArrayによる両頂点配列の有効化を行っていますが、今回は頂点色配列は使用しないので不要になり、また頂点座標配列は選択図形によって異なるので別途指定するので、ここでは頂点座標配列の有効化のみになります。

・テクスチャオブジェクトの解放の修正(OpenGL ES 1.1/2.0共通)

少しでもコードの見通しを良くするため、『GLFun(25)~サンプルコードとの差異』の『テクスチャオブジェクトの有効/無効化』にあるように、3種の単色図形描画時の冒頭にあるテクスチャオブジェクトの解放をglDisableによる無効化に変更します。

・ビューインスタンスへの置換(OpenGL ES 1.1/2.0共通)

頂点座標値を求める演算中、UIKit座標系とOpenGL座標系でY座標の反転を行う際にframeプロパティでビューのフレーム矩形を取得していますが、オリジナルのGLFunではビュークラス(GLFunView)で記述されていたコードのためレシーバが『self』になっているので、上述のビューインスタンスglViewに置換する必要があります。

・頂点配列ポインタの設定(OpenGL ES 2.0用)

OpenGL ES 1.1ではglVertexPointerで頂点配列ポインタを設定しますが、OpenGL ES 2.0では代わりにglVertexAttribPointerで置換します。

3種の図形描画では当該関数の行を置換するだけですが、選択画像がImageの場合はTexture2DクラスのメソッドがOpenGL ES 2.0ではそのまま使えないこともあり、色々書き換える必要があるので別途説明します。

・楕円描画の頂点座標演算の修正(OpenGL ES 1.1/2.0共通)

少しでもコードの見通しを良くするため、『GLFun(25)~サンプルコードとの差異』の『楕円描画の頂点座標生成』にあるように、楕円の半径と原点移動の演算を簡略化しています。

・OpenGL ES 2.0でのテクスチャの貼り付け(OpenGL ES 2.0用)

オリジナルのGLFunでは、画像をテクスチャにしてプリミティブに貼り付けて描画を行う作業はTexture2DクラスのdrawAtPointメソッドで行っていました。
(『GLFun(23)~Texture2Dクラス(12)』参照)

しかしOpenGL ES 2.0の場合、頂点属性配列のポインタをシェーダプログラムに渡す都合上、drawAtPointメソッドに描画をさせるのは色々と面倒なので使用せず、drawFrameメソッド側で描画を行うことにします。

最初にOpenGL ES 2.0を使用しているかどうかを示す、フラグのプロパティuseOpenGLES2をYESにします。

そして指定した画像からテクスチャオブジェクトを生成するTexture2DクラスのinitWithImage:メソッドを呼び出します。
(『GLFun(19)~Texture2Dクラス(8)』参照)

この際に新たに追加した引数として、フラグuseOpenGLES2を渡すようにしています。
(Texture2Dクラスの修正については別途後述します)

これはTexture2Dのインスタンスを生成する際に、暗黙的に呼び出されるTexture2Dクラスのイニシャライザinitializeメソッド内で、OpenGL ES 1.1のみに存在する関数(glTexEnvglEnableClientState)を呼び出しているので、これらの関数呼び出しを回避するために使用します。
(『GLFun(14)~Texture2Dクラス(3)』参照)

その後drawAtPoint:メソッドを呼び出す代わりに、その内容をOpenGL ES 2.0に対応する形式で記述します。

タッチ座標pointは、drawAtPoint:メソッド呼び出し時に渡していた引数そのままの式です。

テクスチャ座標配列coordinates[ ]では、テクスチャ座標系の比を表すプライベートなインスタンス変数(_maxSと_maxT)の代わりに、読み込みのみのプロパティ(maxSとmaxT)を使用しています。

画像の幅widthと高さheightでも、プライベートなインスタンス変数(_widthと_height)の代わりに。読み込みのみプロパティ(pixelsWideとpixelsHigh)を使用しています。

このようにプライベートなインスタンス変数を同内容のプロパティに置き換えているだけで、演算式の内容をそのままコピーしています。

以降の処理に関しては『iPhone/iPadゲーム開発のゲンバから…/OpenGL ES 2.0 ④』を参考に行いました。

正直、内容を理解し切れておらず、ここでの説明には不備や誤りがあると思いますので、元記事を一読することを推奨します。

glUniformによるuniform値の更新では、テクスチャオブジェクト(UNIFORM_TEXTURE)を(おそらく0番目を意味する)0に、(フラグメントシェーダプログラムでの描画色設定の判別用)テクスチャ使用のフラグ(UNIFORM_TEXTURE_FLAG)を1(有効)に設定します。

glVertexAttribPointerによるattribute値の更新では、頂点座標配列(ATTRIB_VERTEX)にvertices、テクスチャ座標配列(ATTRIB_TEXCOORD)にcoordinatesを指定し、glEnableVertexAttribArrayでテクスチャ座標を有効にします。
(頂点座標配列は事前に有効済)

glActiveTextureはアクティブにするテクスチャユニットをしていするもので、(おそらく0番目を意味する)GL_TEXTURE0を指定します。

最後にglDrawArraysでプリミティブを描画します。

glActiveTexture

void glActiveTexture(GLenum texture);

アクティブなテクスチャユニットを選択します。

glActiveTextureは、後続のテクスチャの状態の呼び出しに作用するテクスチャユニットを選択します。

実装がサポートするテクスチャユニット数は実装に依存しますが、少なくとも2である必要があります。

textureがGL_TEXTUREiの一つ(iの範囲は、0から(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1))でない場合、GL_INVALID_ENUMが発生します。

texture:アクティブにするテクスチャユニットを指定します。
テクスチャユニット数は実装に依存しますが、少なくとも2である必要があります。
textureはGL_TEXTUREiの一つ(iの範囲は、0から(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1))である必要があります。
初期値はGL_TEXTURE0です。


OpenGL ES 1.1用のコード部分)

・正射影行列用配列の設定(OpenGL ES 1.1用)

オリジナルのGLFunではOpenGLES2DViewで行っている投影行列へのglOrthoでの正射影行列の適用を追加します。

・描画色の設定(OpenGL ES 1.1用)

glColorによる描画色の設定は、CGColorGetComponentsで取得した配列componentsの内容を要素単位で指定しています。

・頂点座標配列と頂点色配列の修正(OpenGL ES 1.1用)

テンプレートの頂点座標配列と頂点色配列の設定部分ですが、頂点座標配列は各図形描画部分で行うので、配列を有効にするglEnableClientStateのみ残します。

描画色は頂点ごとに設定しませんので、頂点色配列は不要になります。

・画像描画の修正(OpenGL ES 1.1用)

OpenGL ES 2.0での画像描画用に作成したフラグuseOpenGLES2をNOに設定すると共に、initWithImage:メソッドに引数『version:useOpenGLES2』を追加します。


OpenGL ES 1.1と2.0の共通部分)

テンプレートのglDrawArraysは不要ですので削除します。


5)loadShadersメソッドの編集

attribute値の場所の修正と、uniform値の場所を追加します。
(太字が追加修正した部分)

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;

    // Create shader program.
    program = glCreateProgram();

    // Create and compile vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname])
    {
        NSLog(@"Failed to compile vertex shader");
        return FALSE;
    }

    // Create and compile fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname])
    {
        NSLog(@"Failed to compile fragment shader");
        return FALSE;
    }

    // Attach vertex shader to program.
    glAttachShader(program, vertShader);

    // Attach fragment shader to program.
    glAttachShader(program, fragShader);

    // Bind attribute locations.
    // This needs to be done prior to linking.

    glBindAttribLocation(program, ATTRIB_VERTEX, "position");
    glBindAttribLocation(program, ATTRIB_TEXCOORD, "texcoord");

    // Link program.
    if (![self linkProgram:program])
    {
        NSLog(@"Failed to link program: %d", program);

        if (vertShader)
        {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader)
        {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (program)
        {
            glDeleteProgram(program);
            program = 0;
        }

        return FALSE;
    }

    // Get uniform locations.
    uniforms[UNIFORM_COLOR] = glGetUniformLocation(program, "constColor");
    uniforms[UNIFORM_PROJECTION_MATRIX] = glGetUniformLocation(program,
        "projectionMatrix");
    uniforms[UNIFORM_TEXTURE_FLAG] = glGetUniformLocation(program, "flag");
    uniforms[UNIFORM_TEXTURE] = glGetUniformLocation(program, "texture");


    // Release vertex and fragment shaders.
    if (vertShader)
        glDeleteShader(vertShader);
    if (fragShader)
        glDeleteShader(fragShader);

    return TRUE;
}

7805

attribute値の場所では、頂点色を示すATTRIB_COLORが不要なので削除し、新たにテクスチャ座標を示すATTRIB_TEXCOORDを追加します。

uniform値の場所はglGetUniformLocationを使用して、描画色UNIFORM_COLORと正射影行列UNIFORM_PROJECTION_MATRIX、テクスチャ使用のフラグUNIFORM_TEXTURE_FLAG、そしてテクスチャオブジェクトUNIFORM_TEXTUREを指定します。


6)タッチイベントメソッドの編集

3つのタッチイベントメソッド内のビュー指定の修正と、描画メソッドの呼び出しの修正を行います。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    EAGLView *glView = (EAGLView *)self.view;

    if (useRandomColor)
        self.currentColor = [UIColor randomColor];
    UITouch *touch = [[event touchesForView:glView] anyObject];
    firstTouch = [touch locationInView:glView];
    lastTouch = [touch locationInView:glView];

    [self drawFrame];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    EAGLView *glView = (EAGLView *)self.view;

    UITouch *touch = [touches anyObject];
    lastTouch = [touch locationInView:glView];
    [self drawFrame];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    EAGLView *glView = (EAGLView *)self.view;

    UITouch *touch = [touches anyObject];
    lastTouch = [touch locationInView:glView];
    [self drawFrame];
}

7806

3つのタッチイベントメソッド(touchesBegan:withEvent:touchesMoved:withEvent:touchesEnded:withEvent:)は、オリジナルのGLFunではビュークラス(GLFunView)で記述されていたコードのため、touchesForView:メソッドとlocationInView:メソッドのレシーバが『self』になっているので、それぞれビューのインスタンスglViewを生成して置換します。

またタッチイベント発生時に呼び出す描画メソッドが、オリジナルのGLFunではdrawメソッドなのでdrawFrameメソッドに修正します。



参考文献

iPhone/iPadゲーム開発のゲンバから…/OpenGL ES 2.0 ④

OpenGL ES 2.0 Reference Pages

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(30)~OpenGL ESテンプレートの利用(5)

2012. 02. 22
4)drawFrameメソッドの編集

オリジナルのGLFunからコピーした図形描画コードのOpenGL ES 2.0への対応と、実装先がビュークラスからビューコントローラクラスへ移行したことへの対応を行います。
(太字が追加修正した部分)

- (void)drawFrame
{
    [(EAGLView *)self.view setFramebuffer];

    EAGLView *glView = (EAGLView *)self.view;

    // Replace the implementation of this method to do your own custom drawing.
    glClearColor(0.76f, 0.76f, 0.76f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    CGColorRef color = currentColor.CGColor;
    const CGFloat *components = CGColorGetComponents(color);

    if ([context API] == kEAGLRenderingAPIOpenGLES2)
    {
        // Use shader program.
        glUseProgram(program);

        // handle viewing matrices
        GLfloat proj[16];
        // setup projection matrix (orthographic)
        mat4f_LoadOrtho(0.0f, glView.frame.size.width, 0.0f, glView.frame.size.height,
            -1.0f, 1.0f, proj);


        // Update uniform value.
        glUniform4fv(uniforms[UNIFORM_COLOR], 4, components);
        glUniformMatrix4fv(uniforms[UNIFORM_PROJECTION_MATRIX], 1, GL_FALSE,
            proj);
        glUniform1i(uniforms[UNIFORM_TEXTURE_FLAG], 0);


        // Update attribute values.
        glEnableVertexAttribArray(ATTRIB_VERTEX);

        switch (shapeType) {
            case kLineShape: {
                glDisable(GL_TEXTURE_2D);

                GLfloat vertices[4];
                // Convert coordinates
                vertices[0] = firstTouch.x;
                vertices[1] = glView.frame.size.height - firstTouch.y;
                vertices[2] = lastTouch.x;
                vertices[3] = glView.frame.size.height - lastTouch.y;
                glLineWidth(2.0);

                // Update attribute values.
                glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);

                glDrawArrays(GL_LINES, 0, 2);
                break;
            }
            case kRectShape: {
                glDisable(GL_TEXTURE_2D);

                // Calculate bounding rect and store in vertices
                GLfloat vertices[8];
                GLfloat minX = (firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x;
                GLfloat minY =
                (glView.frame.size.height-firstTouch.y>glView.frame.size.height-lastTouch.y)
                ? glView.frame.size.height-lastTouch.y : glView.frame.size.height-firstTouch.y;

                GLfloat maxX = (firstTouch.x > lastTouch.x) ? firstTouch.x : lastTouch.x;
                GLfloat maxY =
                (glView.frame.size.height-firstTouch.y>glView.frame.size.height-lastTouch.y)
                ? glView.frame.size.height-firstTouch.y : glView.frame.size.height-lastTouch.y;

                vertices[0] = maxX;
                vertices[1] = maxY;
                vertices[2] = minX;
                vertices[3] = maxY;
                vertices[4] = minX;
                vertices[5] = minY;
                vertices[6] = maxX;
                vertices[7] = minY;

                // Update attribute values.
                glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);

                glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
                break;
            }
            case kEllipseShape: {
                glDisable(GL_TEXTURE_2D);

                GLfloat vertices[720];
                GLfloat xradius = fabsf((firstTouch.x - lastTouch.x) / 2);
                GLfloat yradius = fabsf((firstTouch.y - lastTouch.y) / 2);

                for (int i = 0; i <= 720; i+=2) {
                    GLfloat xOffset =
                    (firstTouch.x > lastTouch.x) ? lastTouch.x + xradius : firstTouch.x + xradius;
                    GLfloat yOffset = (firstTouch.y < lastTouch.y) ?
                        glView.frame.size.height - lastTouch.y + yradius :
                        glView.frame.size.height - firstTouch.y + yradius;

                    vertices[i] = (cos(degreesToRadian(i / 2)) * xradius) + xOffset;
                    vertices[i+1] = (sin(degreesToRadian(i / 2)) * yradius) + yOffset;
                }

                // Update attribute values.
                glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);

                glDrawArrays(GL_TRIANGLE_FAN, 0, 360);
                break;
            }
            case kImageShape:
                useOpenGLES2 = YES;

                if (sprite == nil) {
                    self.sprite = [[Texture2D alloc] initWithImage:[UIImage
                    imageNamed:@"iphone.png"] version:useOpenGLES2];

                    glBindTexture(GL_TEXTURE_2D, sprite.name);
                }
                glEnable(GL_TEXTURE_2D);

                CGPoint point =
                CGPointMake(lastTouch.x, glView.frame.size.height - lastTouch.y);
                GLfloat coordinates[] = {0, sprite.maxT, sprite.maxS, sprite.maxT, 0, 0,
                sprite.maxS, 0};
                GLfloat width = (GLfloat)sprite.pixelsWide * sprite.maxS;
                GLfloat height = (GLfloat)sprite.pixelsHigh * sprite.maxT;
                GLfloat vertices[] = {-width / 2 + point.x, -height / 2 + point.y,
                    width / 2 + point.x, -height / 2 + point.y,
                    -width / 2 + point.x, height / 2 + point.y,
                    width / 2 + point.x, height / 2 + point.y,};


                // Update uniform value.
                glUniform1i(uniforms[UNIFORM_TEXTURE], 0);
                glUniform1i(uniforms[UNIFORM_TEXTURE_FLAG], 1);


                // Update attribute values.
                glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertices);
                glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, 0, 0, coordinates);
                glEnableVertexAttribArray(ATTRIB_TEXCOORD);

                glActiveTexture(GL_TEXTURE0);


                glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
                break;
            default:
                break;
        }

        // Validate program before drawing. This is a good check, but only really necessary in a
        // debug build. DEBUG macro must be defined in your debug configurations if that's not
        // already the case.

#if defined(DEBUG)
        if (![self validateProgram:program])
        {
            NSLog(@"Failed to validate program: %d", program);
            return;
        }
#endif
    }
    else
    {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrthof(0, glView.frame.size.width, 0, glView.frame.size.height, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glColor4f(components[0], components[1], components[2], components[3]);

        glEnableClientState(GL_VERTEX_ARRAY);

        switch (shapeType) {
            case kLineShape: {
                glDisable(GL_TEXTURE_2D);

                GLfloat vertices[4];
                // Convert coordinates
                vertices[0] = firstTouch.x;
                vertices[1] = glView.frame.size.height - firstTouch.y;
                vertices[2] = lastTouch.x;
                vertices[3] = glView.frame.size.height - lastTouch.y;
                glLineWidth(2.0);
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_LINES, 0, 2);
                break;
            }
            case kRectShape: {
                glDisable(GL_TEXTURE_2D);

                // Calculate bounding rect and store in vertices
                GLfloat vertices[8];
                GLfloat minX = (firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x;
                GLfloat minY =
                (glView.frame.size.height-firstTouch.y>glView.frame.size.height-lastTouch.y)
                ? glView.frame.size.height-lastTouch.y : glView.frame.size.height-firstTouch.y;

                GLfloat maxX = (firstTouch.x > lastTouch.x) ? firstTouch.x : lastTouch.x;
                GLfloat maxY =
                (glView.frame.size.height-firstTouch.y>glView.frame.size.height-lastTouch.y)
                ? glView.frame.size.height-firstTouch.y : glView.frame.size.height-lastTouch.y;

                vertices[0] = maxX;
                vertices[1] = maxY;
                vertices[2] = minX;
                vertices[3] = maxY;
                vertices[4] = minX;
                vertices[5] = minY;
                vertices[6] = maxX;
                vertices[7] = minY;
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
                break;
            }
            case kEllipseShape: {
                glDisable(GL_TEXTURE_2D);

                GLfloat vertices[720];
                GLfloat xradius = fabsf((firstTouch.x - lastTouch.x) / 2);
                GLfloat yradius = fabsf((firstTouch.y - lastTouch.y) / 2);

                for (int i = 0; i <= 720; i+=2) {
                    GLfloat xOffset =
                    (firstTouch.x > lastTouch.x) ? lastTouch.x + xradius : firstTouch.x + xradius;
                    GLfloat yOffset = (firstTouch.y < lastTouch.y) ?
                        glView.frame.size.height - lastTouch.y + yradius :
                        glView.frame.size.height - firstTouch.y + yradius;

                    vertices[i] = (cos(degreesToRadian(i / 2)) * xradius) + xOffset;
                    vertices[i+1] = (sin(degreesToRadian(i / 2)) * yradius) + yOffset;
                }
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 360);
                break;
            }
            case kImageShape:
                useOpenGLES2 = NO;

                if (sprite == nil) {
                    self.sprite = [[Texture2D alloc] initWithImage:[UIImage
                    imageNamed:@"iphone.png"] version:useOpenGLES2];

                    glBindTexture(GL_TEXTURE_2D, sprite.name);
                }
                glEnable(GL_TEXTURE_2D);
                [sprite drawAtPoint:CGPointMake(lastTouch.x, glView.frame.size.height
                - lastTouch.y)];

                break;
            default:
                break;
        }
    }

    [(EAGLView *)self.view presentFramebuffer];
}

7804

コードの記述順に変更点を説明しますが、OpenGL ES 1.1と2.0で共通する部分と専用の変更がありますので注意してください。

OpenGL ES 1.1と2.0の共通部分)

・ビュークラスのインスタンスの作成(OpenGL ES 1.1/2.0共通)

正射影行列の作成やUIKit座標系からOpenGL座標系へのY座標反転に使用する画面サイズの取得のため、viewプロパティでEAGLViewクラスのインスタンスglViewを作成します。

・色バッファのクリア値の修正(OpenGL ES 1.1/2.0共通)

テンプレートのnibファイルGLFunViewController.xibのビューの色に合わせるため、背景色(色バッファのクリア値)を0.76fグレーに修正します。



参考文献

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(29)~OpenGL ESテンプレートの利用(4)

2012. 02. 21
●matrixクラスの追加

OpenGL ES Applicationテンプレートにおける投影変換はglLoadIdentityによる初期化のみで、正規化デバイス座標系と視点座標系で変化がありませんが、オリジナルのGLFunでは投影変換にglOrthoによる正射影行列を適用しています。
(『GLFun(4)~OpenGL ESへの対応と作業工程』参照)

しかしOpenGL ES 2.0ではglOrthoなどの行列変換関数は用意されていませんので、自前で変換行列を揃える必要があります。

ここではApple公式のサンプルコード『GLES2Sample』にあるmatrixクラス(拡大縮小、回転、平行移動、正射影、透視投影行列の関数群)を利用します。

サンプルコードGLES2Sampleをダウンロードし、Classesフォルダ内にあるmatrix.hとmatrix.cの2つのファイルをサンプルコード版のGLFunプロジェクトのClassesフォルダにコピーし、プロジェクトのOther Sources下に追加します。

そしてGLFunViewController.mでヘッダファイルmatrix.hをインポートします。

#import "UIColor-Random.h"

7800


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

描画を行うGLFunViewControllerに、OpenGL ES 2.0を利用しているかどうかを示すプロパティuseOpenGLES2を追加します。

これはOpenGL ES 1.1のみに対応しているTexture2Dクラスのメソッドを、OpenGL ES 2.0でも利用できるようにするためのフラグとして独自に追加するものです。

(太字が追加した部分)

#import <UIKit/UIKit.h>

#import <OpenGLES/EAGL.h>

#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

#import "Constants.h"

#import "Texture2D.h"

@interface GLFunViewController : UIViewController
{
    EAGLContext *context;
    GLuint program;

    UISegmentedControl *colorControl;

    CGPoint firstTouch;
    CGPoint lastTouch;
    UIColor *currentColor;
    BOOL useRandomColor;
    ShapeType shapeType;
    Texture2D *sprite;

    BOOL useOpenGLES2;
}

@property (nonatomic, retain) IBOutlet UISegmentedControl *colorControl;

@property CGPoint firstTouch;
@property CGPoint lastTouch;
@property (nonatomic, retain) UIColor *currentColor;
@property BOOL useRandomColor;
@property ShapeType shapeType;
@property (nonatomic, retain) Texture2D *sprite;

@property BOOL useOpenGLES2;

- (IBAction)changeColor:(id)sender;
- (IBAction)changeShape:(id)sender;

@end

7801


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

1)uniformインデックスの追加

プログラマブルシェーダでのプリミティブの描画時に固定となる値を宣言します。
(太字が追加した部分)

// Uniform index.
enum {
    UNIFORM_COLOR,
    UNIFORM_PROJECTION_MATRIX,
    UNIFORM_TEXTURE_FLAG,
    UNIFORM_TEXTURE,

    NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];

UNIFORM_COLOR図形の描画色
UNIFORM_PROJECTION_MATRIX正射影行列
UNIFORM_TEXTURE_FLAGテクスチャ使用の有無を示すフラグ
UNIFORM_TEXTUREテクスチャ画像

図形の描画色はテンプレートのように頂点ごとに異なるわけではなく単色なのでuniform値で渡します。

正射影行列はオリジナルのGLFunにおけるglOrthoと同じ行列を渡し、頂点シェーダプログラムで適用します。

テクスチャ使用の有無を示すフラグはフラグメントシェーダプログラムで、テクスチャを使用しない3種の図形の場合とテクスチャを使用する画像の描画の場合で異なる描画色の式を利用するため、その判別に使用します。

テクスチャ画像は画像データを保持し、テクスチャ使用時にフラグメントシェーダプログラムでの描画色の式で使用されます。


2)attributeインデックスの変更

プログラマブルシェーダでの頂点属性の値を宣言します。
(太字が修正した部分)

// Attribute index.
enum {
    ATTRIB_VERTEX,
    ATTRIB_TEXCOORD,
    NUM_ATTRIBUTES
};

テンプレートでは頂点ごとに異なる色が設定されていましたが、ここではプリミティブ全体を単色で塗りつぶすので色はuniform値で指定することになり、ATTRIB_COLORは不要になります。

その代わりテクスチャを貼り付ける為に、頂点座標に対応するテクスチャ座標を示すATTRIB_TEXCOORDを追加しています。


3)useOpenGLES2プロパティの実装

Texture2DクラスのメソッドをOpenGL ES 2.0に対応させるにあたって、メソッド内部でのOpenGL ES 1.1と2.0の分岐に使用するフラグとして、プロパティuseOpenGLES2を実装します。

(太字が追加した部分)

@synthesize context;

@synthesize colorControl;

@synthesize firstTouch;
@synthesize lastTouch;
@synthesize currentColor;
@synthesize useRandomColor;
@synthesize shapeType;
@synthesize sprite;

@synthesize useOpenGLES2;

7802



参考文献

Apple/GLES2Sample

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る

GLFun(28)~OpenGL ESテンプレートの利用(3)

2012. 02. 20
●GLFunView.mソースファイルからの移植

オリジナルのGLFunViewController.mから、ヘッダファイルで宣言したプロパティの実装と選択色の設定、図形描画のコード、そして3つのタッチイベントのメソッドをコピーします。
(太字が追加した部分)

(前略)

@synthesize firstTouch;
@synthesize lastTouch;
@synthesize currentColor;
@synthesize useRandomColor;
@synthesize shapeType;
@synthesize sprite;


- (void)awakeFromNib
{
    EAGLContext *aContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

    if (!aContext)
    {
        aContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
    }

    if (!aContext)
        NSLog(@"Failed to create ES context");
    else if (![EAGLContext setCurrentContext:aContext])
        NSLog(@"Failed to set ES context current");

    self.context = aContext;
    [aContext release];

    [(EAGLView *)self.view setContext:context];
    [(EAGLView *)self.view setFramebuffer];

    if ([context API] == kEAGLRenderingAPIOpenGLES2)
        [self loadShaders];

    self.currentColor = [UIColor redColor];
    self.useRandomColor = NO;

}

- (void)dealloc
{
    if (program)
    {
        glDeleteProgram(program);
        program = 0;
    }

    // Tear down context.
    if ([EAGLContext currentContext] == context)
        [EAGLContext setCurrentContext:nil];

    [context release];

    [colorControl release];

    [currentColor release];
    [sprite release];


    [super dealloc];
}

(中略)

- (void)drawFrame
{
    [(EAGLView *)self.view setFramebuffer];

    // Replace the implementation of this method to do your own custom drawing.
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    CGColorRef color = currentColor.CGColor;
    const CGFloat *components = CGColorGetComponents(color);


    if ([context API] == kEAGLRenderingAPIOpenGLES2)
    {
        // Use shader program.
        glUseProgram(program);

        // Update uniform value.

        // Update attribute values.
        glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
        glEnableVertexAttribArray(ATTRIB_VERTEX);
        glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, squareColors);
        glEnableVertexAttribArray(ATTRIB_COLOR);

        switch (shapeType) {
            case kLineShape: {
                if (sprite) {
                    [sprite release];
                    self.sprite = nil;
                }
                GLfloat vertices[4];
                // Convert coordinates
                vertices[0] = firstTouch.x;
                vertices[1] = self.frame.size.height - firstTouch.y;
                vertices[2] = lastTouch.x;
                vertices[3] = self.frame.size.height - lastTouch.y;
                glLineWidth(2.0);
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_LINES, 0, 2);
                break;
            }
            case kRectShape: {
                if (sprite) {
                    [sprite release];
                    self.sprite = nil;
                }
                // Calculate bounding rect and store in vertices
                GLfloat vertices[8];
                GLfloat minX = (firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x;
                GLfloat minY =
                    (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y)
                    ? self.frame.size.height - lastTouch.y : self.frame.size.height - firstTouch.y;
                GLfloat maxX = (firstTouch.x > lastTouch.x) ? firstTouch.x : lastTouch.x;
                GLfloat maxY =
                    (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y)
                    ? self.frame.size.height - firstTouch.y : self.frame.size.height - lastTouch.y;
                vertices[0] = maxX;
                vertices[1] = maxY;
                vertices[2] = minX;
                vertices[3] = maxY;
                vertices[4] = minX;
                vertices[5] = minY;
                vertices[6] = maxX;
                vertices[7] = minY;
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
                break;
            }
            case kEllipseShape: {
                if (sprite) {
                    [sprite release];
                    self.sprite = nil;
                }
                GLfloat vertices[720];
                GLfloat xradius = (firstTouch.x > lastTouch.x) ?
                    (firstTouch.x - lastTouch.x)/2 : (lastTouch.x - firstTouch.x)/2;
                GLfloat yradius =
                (self.frame.size.height-firstTouch.y > self.frame.size.height-lastTouch.y) ?
                ((self.frame.size.height-firstTouch.y)-(self.frame.size.height-lastTouch.y))/2 :
                ((self.frame.size.height-lastTouch.y)-(self.frame.size.height-firstTouch.y))/2;
                for (int i = 0; i <= 720; i+=2) {
                    GLfloat xOffset =
                    (firstTouch.x>lastTouch.x) ? lastTouch.x + xradius : firstTouch.x + xradius;
                    GLfloat yOffset =
                    (self.frame.size.height-firstTouch.y > self.frame.size.height-lastTouch.y) ?
                    self.frame.size.height - lastTouch.y + yradius :
                    self.frame.size.height - firstTouch.y + yradius;
                    vertices[i] = (cos(degreesToRadian(i / 2)) * xradius) + xOffset;
                    vertices[i+1] = (sin(degreesToRadian(i / 2)) * yradius) + yOffset;
                }
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 360);
                break;
            }
            case kImageShape:
                if (sprite == nil) {
                    self.sprite = [[Texture2D alloc]
                            initWithImage:[UIImage imageNamed:@"iphone.png"]];
                    glBindTexture(GL_TEXTURE_2D, sprite.name);
                }
                glEnable(GL_TEXTURE_2D);
                [sprite
                drawAtPoint:CGPointMake(lastTouch.x, self.frame.size.height - lastTouch.y)];
                break;
            default:
                break;
        }


        // Validate program before drawing. This is a good check, but only really necessary in a
        // debug build. DEBUG macro must be defined in your debug configurations if that's not
        // already the case.

#if defined(DEBUG)
        if (![self validateProgram:program])
        {
            NSLog(@"Failed to validate program: %d", program);
            return;
        }
#endif
    }
    else
    {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glVertexPointer(2, GL_FLOAT, 0, squareVertices);
        glEnableClientState(GL_VERTEX_ARRAY);
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
        glEnableClientState(GL_COLOR_ARRAY);

        switch (shapeType) {
            case kLineShape: {
                if (sprite) {
                    [sprite release];
                    self.sprite = nil;
                }
                GLfloat vertices[4];
                // Convert coordinates
                vertices[0] = firstTouch.x;
                vertices[1] = self.frame.size.height - firstTouch.y;
                vertices[2] = lastTouch.x;
                vertices[3] = self.frame.size.height - lastTouch.y;
                glLineWidth(2.0);
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_LINES, 0, 2);
                break;
            }
            case kRectShape: {
                if (sprite) {
                    [sprite release];
                    self.sprite = nil;
                }
                // Calculate bounding rect and store in vertices
                GLfloat vertices[8];
                GLfloat minX = (firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x;
                GLfloat minY =
                    (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y)
                    ? self.frame.size.height - lastTouch.y : self.frame.size.height - firstTouch.y;
                GLfloat maxX = (firstTouch.x > lastTouch.x) ? firstTouch.x : lastTouch.x;
                GLfloat maxY =
                    (self.frame.size.height - firstTouch.y > self.frame.size.height - lastTouch.y)
                    ? self.frame.size.height - firstTouch.y : self.frame.size.height - lastTouch.y;
                vertices[0] = maxX;
                vertices[1] = maxY;
                vertices[2] = minX;
                vertices[3] = maxY;
                vertices[4] = minX;
                vertices[5] = minY;
                vertices[6] = maxX;
                vertices[7] = minY;
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
                break;
            }
            case kEllipseShape: {
                if (sprite) {
                    [sprite release];
                    self.sprite = nil;
                }
                GLfloat vertices[720];
                GLfloat xradius = (firstTouch.x > lastTouch.x) ?
                    (firstTouch.x - lastTouch.x)/2 : (lastTouch.x - firstTouch.x)/2;
                GLfloat yradius =
                (self.frame.size.height-firstTouch.y > self.frame.size.height-lastTouch.y) ?
                ((self.frame.size.height-firstTouch.y)-(self.frame.size.height-lastTouch.y))/2 :
                ((self.frame.size.height-lastTouch.y)-(self.frame.size.height-firstTouch.y))/2;
                for (int i = 0; i <= 720; i+=2) {
                    GLfloat xOffset =
                    (firstTouch.x>lastTouch.x) ? lastTouch.x + xradius : firstTouch.x + xradius;
                    GLfloat yOffset =
                    (self.frame.size.height-firstTouch.y > self.frame.size.height-lastTouch.y) ?
                    self.frame.size.height - lastTouch.y + yradius :
                    self.frame.size.height - firstTouch.y + yradius;
                    vertices[i] = (cos(degreesToRadian(i / 2)) * xradius) + xOffset;
                    vertices[i+1] = (sin(degreesToRadian(i / 2)) * yradius) + yOffset;
                }
                glVertexPointer(2, GL_FLOAT, 0, vertices);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 360);
                break;
            }
            case kImageShape:
                if (sprite == nil) {
                    self.sprite = [[Texture2D alloc]
                            initWithImage:[UIImage imageNamed:@"iphone.png"]];
                    glBindTexture(GL_TEXTURE_2D, sprite.name);
                }
                glEnable(GL_TEXTURE_2D);
                [sprite
                drawAtPoint:CGPointMake(lastTouch.x, self.frame.size.height - lastTouch.y)];
                break;
            default:
                break;
        }

     }
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    [(EAGLView *)self.view presentFramebuffer];
}

(中略)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (useRandomColor)
        self.currentColor = [UIColor randomColor];
    UITouch *touch = [[event touchesForView:self] anyObject];
    firstTouch = [touch locationInView:self];
    lastTouch = [touch locationInView:self];
    [self draw];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    lastTouch = [touch locationInView:self];
    [self draw];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    lastTouch = [touch locationInView:self];
    [self draw];
}


@end

7799

コピーして追加する項目は下記の通りです。

・ヘッダファイルで宣言を追加した6つのプロパティ

・選択色の初期設定(オリジナル:initWithCoder:メソッド → awakeFromNibメソッド)

・deallocメソッドでのプロパティの解放(6つのプロパティの内、Objective-Cオブジェクトの2つ)

・選択色の設定と図形描画(オリジナル:drawメソッド → drawFrameメソッド)
 (図形描画のswitch文はOpenGL ES 2.0と1.1のif~else文内の両方に追加)

・タッチイベントを検出して対応する3つのメソッド



参考文献

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る

Beginning Ios 6 Development: Exploring the Ios SdkBeginning Ios 6 Development: Exploring the Ios Sdk
(2012/12/26)
David Mark、Jack Nutting 他

商品詳細を見る






Bose QuietComfort 20
Calendar
01 | 2012/02 | 03
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 - - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

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

Wish List
WACOM


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

Bose QuietComfort 20
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