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

2012. 03. 10
CheckPleaseはv字型のチェックマークのようなカスタムジェスチャを検出してラベルに表示します。

7887

判定条件はかなり緩く、
・直前の線分と現在の線分間の角度が50~135度の範囲
・ドラッグの総距離が10ピクセル以上
となっています。

ただ『直前の線分と現在の線分』がピクセル単位での移動の軌跡を指しているので、直線から少しずれただけでも誤検出してしまうほど過敏です。

実用的には認識範囲を狭めたり、ある程度の長さを持った線分間の角度を比較するなどの改良が必要になると思われますが、ここでは本書ならびにサンプルコードの条件で説明します。


●プロジェクトの作成

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

7815

7888


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

カスタムジェスチャを認識するための定数と3つの変数、認識したピンチを表示するラベルのアウトレット、そしてラベルを表示してから一定時間後にラベルを消去するメソッドの宣言を行います。
(太字が追加した部分)

#define kMinimumCheckMarkAngle 50
#define kMaximumCheckMarkAngle 135
#define kMinimumCheckMarkLength 10


#import <UIKit/UIKit.h>

@interface CheckPleaseViewController : UIViewController {
    UILabel *label;
    CGPoint lastPreviousPoint;
    CGPoint lastCurrentPoint;
    CGFloat lineLengthSoFar;

}

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

- (void)eraseLabel;


@end

7889


1)定数

v字型のチェックマークのようなカスタムジェスチャを認識するのに必要な定数で、定数kMinimumCheckMarkAngleは最小角度、定数kMaximumCheckMarkAngleは最大角度、定数kMinimumCheckMarkLengthは最小移動距離を示します。


2)アウトレット

カスタムジェスチャを検出した際に表示するラベルlabelをアウトレットとして宣言します。


3)消去メソッド


各ラベルを表示から一定時間後に消去するメソッドeraseLabel:を宣言します。


●CheckPleaseViewController.xibの編集

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


1)ラベルの追加

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

7870


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

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

7871


3)ラベルのサイズ変更

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


4)ラベルの属性変更

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

Label)

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

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

View)

・Mode
サンプルコードではLeftではなくScale To Fillになっていますが、今回は関係無いのでそのままにしています。

7872


5)ビューの属性

今回のジェスチャはシングルタッチですので、ビューの属性は変更する必要がありません。

7883

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


●CGPointUtilsクラスの追加

PinchMe(1)~イベント処理メソッド』で使用したCGPointUtilsクラスを今回も使用します。

原著であるApress社のiOS 3.x版のサンプルコードの『13 CheckPlease』のClassesフォルダ下にあるCGPointUtils.hとCGPointUtils.cファイルを自身のCheckPleaseプロジェクトのClassesフォルダにコピーし、プロジェクトに追加します。


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

CGPointUtils.hヘッダファイルのインポートと、CheckPleaseViewController.hヘッダファイルで宣言したプロパティlabelとラベルを消去するeraseLabelメソッドの実装、そして2つのタッチイベント処理メソッドを追加します。
(太字が追加した部分)

#import "CheckPleaseViewController.h"
#import "CGPointUtils.h"

@implementation CheckPleaseViewController

@synthesize label;

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


// 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];
    CGPoint point = [touch locationInView:self.view];
    lastPreviousPoint = point;
    lastCurrentPoint = point;
    lineLengthSoFar = 0.0f;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint previousPoint = [touch previousLocationInView:self.view];
    CGPoint currentPoint = [touch locationInView:self.view];
    CGFloat angle = angleBetweenLines(lastPreviousPoint, lastCurrentPoint,
        previousPoint, currentPoint);

    if (angle >= kMinimumCheckMarkAngle && angle <= kMaximumCheckMarkAngle &&
        lineLengthSoFar > kMinimumCheckMarkLength) {
            label.text = @"Checkmark";
            [self performSelector:@selector(eraseLabel) withObject:nil afterDelay:1.6];
    }

    lineLengthSoFar += distanceBetweenPoints(previousPoint, currentPoint);
    lastPreviousPoint = previousPoint;
    lastCurrentPoint = currentPoint;
}


@end

7890


1)CGPointUtils.hヘッダファイルのインポート

カスタムジェスチャを認識するため、ドラッグしているタッチポイント間の距離と角度の計算にCGPointUtilsクラスの関数を使用するので、CGPointUtils.hヘッダファイルをインポートします。


2)プロパティの実装

CheckPleaseViewController.hヘッダファイルで宣言したプロパティlabelを@synthesizeで実装します。


3)eraseLabel:メソッド

eraseLabel:メソッドはラベルのテキストを消去するメソッドで、touchesMoved:withEvent:メソッド内でカスタムジェスチャを認識してラベルを表示した後に呼び出されます。

テキストの消去はtextプロパティで文字列を空に設定しているだけです。


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

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


5)shouldAutorotateToInterfaceOrientation:メソッド

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


6)viewDidUnloadメソッド

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


7)deallocメソッド

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


8)touchesBegan:withEvent:メソッド

touchesBegan:withEvent:メソッドはビューに一本以上の指が触れた時に呼び出されるメソッドで、ここではタッチポイント座標から基準となる線分の座標lastPreviousPointとlastCurrentPointと、ジェスチャの移動距離lineLengthSoFarの初期化を行っています。

タッチオブジェクトのセットtouchesからanyObjectメソッドで適切なオブジェクトを選抜し、タッチオブジェクトからlocationInView:メソッドでタッチポイントの座標pointを取得します。

取得した座標pointは、基準となる線分の座標lastPreviousPointとlastCurrentPoint双方に設定し、ジェスチャの移動距離lineLengthSoFarは0.0に設定します。


9)touchesMoved:withEvent:メソッド

touchesMoved:withEvent:メソッドはビューで一本以上の指が移動した時に呼び出されるメソッドで、ここでは保持している移動前の線分と移動した後の線分から線分間の角度を求め、移動距離と合わせてカスタムジェスチャかどうかの判定を行います。

タッチオブジェクトのセットtouchesからanyObjectメソッドで適切なオブジェクトを選抜します。

タッチオブジェクトからは、previousLocationView:メソッドで以前の位置PreviousPointとlocationInView:メソッドで現在位置currentPointを取得します。

以前の位置と現在位置から2本の線分を導出し、線分間の角度angleをCGPointUtilsクラスのangleBetweenLines関数で求めます。
(angleBetweenLines関数については『PinchMe(1)~イベント処理メソッド』参照)

角度angleが定数で指定した50~130度の範囲内で、且つ移動距離lineLengthSoFarが定数で指定した10ピクセル以上だった場合はチェックマークのジェスチャと判断し、textプロパティでラベルの文字列を設定して表示した後、performSelector:withObject:afterDelay:メソッドで1.6秒後にeraseLabelメソッドを呼び出すことによって文字列を消去しています。

その後、以前の位置と現在位置間の距離をCGPointUtilsクラスのdistanceBetweenPoints関数で求め、移動距離lineLengthSoFarをインクリメントします。
(distanceBetweenPoints関数については『PinchMe(1)~イベント処理メソッド』参照)

また、前回の以前の位置lastPreviousPointと前回の現在位置lastCurrentPointを、それぞれ以前の位置previousPointと現在位置currentPointで更新します。

コードをそのまま説明しても分かり難いので、具体例で説明してみます。

最初のtouchesBegan:withEvent:メソッドでの初期化の時点では点は1つしか存在せず、前回の位置lastPreviousPointと前回の現在位置lastCurrentPointは同じ座標を指しています。
(この座標を点Aとします)

 lastPreviousPoint  lastCurrentPoint 
点A点A

次にtouchesMoved:withEvent:メソッドが始めて呼び出された時の座標を点Bとします。

previousLocationInView:メソッドによる以前の位置previousPointは点Aを指し、locationInView:メソッドによる現在位置currentPointは点Bを指します。

 lastPreviousPoint  lastCurrentPoint  previousPoint  currentPoint 
点A点A点A点B

ジェスチャの判定後、前回の座標は現在の座標に更新されます。

 lastPreviousPoint  lastCurrentPoint  previousPoint  currentPoint 
点A点B点A点B

更に移動して2回目のtouchesMoved:withEvent:メソッドが始めて呼び出された時の座標を点Cとすると、以前の位置previousPointは点Bを指し、現在位置currentPointは点Cを指します。

 lastPreviousPoint  lastCurrentPoint  previousPoint  currentPoint 
点A点B点B点C

ここでジェスチャの判定は、『線分ABと線分BCの角度』と『距離ABC』で行うことになります。

ジェスチャの判定後、前回の座標は現在の座標に更新されます。

 lastPreviousPoint  lastCurrentPoint  previousPoint  currentPoint 
点B点C点B点C

以降『線分BCと線分CDの角度』と『距離ABCD』、『線分CDと線分DEの角度』と『距離ABCDE』・・・と続いて行きます。

線分と言っても、touchesMoved:withEvent:メソッドはピクセル単位の移動毎に呼び出されるので、スワイプを行おうとすると(移動距離が10ピクセルを超えてからは)ほんの僅かずれただけでチェックマークと判定されます。

previousLocationInView:

- (CGPoint)previousLocationInView:(UIView *)view

指定されたビューの座標系におけるレシーバの以前の位置を返します。

このメソッドは、指定されたビューの座標系に置けるUITouchオブジェクトの以前の位置を返します。

タッチオブジェクトは他のビューからビューに転送された可能性があるため、このメソッドは必要な指定されたビューの座標系にタッチ位置の変換を実行します。

view:タッチ位置を所望する座標系のビューオブジェクトを指定します。
タッチを処理しているカスタムビューは、自身の座標系内でのタッチ位置を取得ためにselfを指定することができます。
nilを渡すとウィンドウの座標におけるタッチ位置を取得します。



参考文献

iOSイベント処理ガイド

UITouch 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 他

商品詳細を見る






Bose SoundSport wireless headphones
2 Comments
By ff11 09, 2014 - URL [ edit ]

質問してもよろしいでしょうか?

By 水月杏香11 10, 2014 - URL [ edit ]

質問の内容にもよりますが、私が答えられるものであればできるだけ対応します。

Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
02 | 2017/03 | 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 30 31 -
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 SoundSport wireless headphones
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