Created
March 28, 2014 13:25
-
-
Save edom18/9832627 to your computer and use it in GitHub Desktop.
[Objective-C] タップによるズーム機能の実装メモ ref: http://qiita.com/edo_m18/items/9400bf8fa37ceb7077d7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @protocol TapDetectingViewDelegate; | |
| @interface SMPView : UIView | |
| @property (assign, nonatmic) id<TapDetectingViewDelegate> delegate; | |
| @end | |
| @protocol TapDetectingViewDelegate <NSObject> | |
| @optional | |
| - (void)tapDetectingView:(SMPView *)view goSingleTapAtPoint:(CGPoint)tapPoint; | |
| - (void)tapDetectingView:(SMPView *)view goDoubleTapAtPoint:(CGPoint)tapPoint; | |
| - (void)tapDetectingView:(SMPView *)view goTwoFingerTapAtPoint:(CGPoint)tapPoint; | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @interface SMPView () | |
| @property (assign, nonatomic) BOOL multipleTouches; | |
| @property (assign, nonatomic) BOOL twoFingerTapIsPossible; | |
| @property (assign, nonatomic) CGPoint tapLocation; | |
| - (void)handleSingleTap; | |
| - (void)handleDoubleTap; | |
| - (void)handleTwoFingerTap; | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @implementation SMPView | |
| - (id)initWithFrame:(CGRect)frame | |
| { | |
| if (self = [super initWithFrame:frame]) { | |
| // マルチタッチを有効化 | |
| self.multipleTouchEnabled = YES; | |
| self.twoFingerTapIsPossible = NO; | |
| self.multipleTouches = NO; | |
| } | |
| return self; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ---------------------------------------------- | |
| // タッチ制御 | |
| // ---------------------------------------------- | |
| // タッチ開始処理 | |
| - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event | |
| { | |
| // 保留中のhandleSingleTapメッセージがあればそれを取り消す | |
| [NSObject cancelPreviousPerformRequestsWithTarget:self | |
| selector:@selector(handleSingleTap) | |
| object:nil]; | |
| // タッチの状態を更新 | |
| if ([[event touchesForView:self] count] > 1) { | |
| self.multipleTouches = YES; | |
| } | |
| if ([[event touchesForView:self] count] > 2) { | |
| // 指が3本以上触れていたら2本指タップの状態を`NO`に | |
| self.twoFingerTapIsPossible = NO; | |
| } | |
| } | |
| - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event | |
| { | |
| // touches.countは話された指の数 | |
| // [event touchesForView:self].countは、直前までViewに触れていた指の数 | |
| BOOL allTouchesEnded = ([touches count] == [[event touchesForView:self] count]); | |
| // まず単純なシングル/ダブルタップを判定する | |
| // 複数回のタッチが行われていない場合のみ、シングル/ダブルタップの可能性がある | |
| if (!self.multipleTouches) { | |
| UITouch *touch = [touch anyObject]; | |
| self.tapLocation = [touch locationInView:self]; | |
| // シングルタップ | |
| if ([touch tapCount] == 1) { | |
| [self performSelector:@selector(handleSingleTap) | |
| withObject:nil | |
| afterDelay:0.35]; | |
| } | |
| else if ([touch tapCount] == 2) { | |
| [self handleDoubleTap]; | |
| } | |
| } | |
| // 複数回のタッチが行われていた場合は、それが2本指のタップだったかどうか、 | |
| // また、そうである状況が否定されていないか確認する | |
| else if (self.multipleTouches && self.twoFingerTapIsPossible) { | |
| // ケース1: 両方のタッチの同時の終了の場合 | |
| if ([touches count] == 2 && allTouchesEnded) { | |
| int i = 0; | |
| NSUinteger tapCounts[2]; | |
| CGPoint tapLocation[2]; | |
| for (UITouch *touch in touches) { | |
| tapCount[i] = [touch tapCount]; | |
| tapLocation[i] = [touch locationInView:self]; | |
| i++; | |
| } | |
| if (tapCounts[0] == 1 && tapCounts[1] == 1) { | |
| // 両方ともシングルタップであれば、これは2本指のタップ | |
| self.tapLocation = midpointBetweenPoints(tapLocations[0], tapLocations[1]); | |
| [self handleTwoFingerTap]; | |
| } | |
| } | |
| // ケース2: これが1回のタッチの終了で、もう1つはまだ終了していない場合 | |
| else if ([touches count] == 1 && !allTouchesEnded) { | |
| UITouch *touch = [touches anyObject]; | |
| if ([touch tapCount] == 1) { | |
| // タッチが1回のタップであれば、その位置を保存し、 | |
| // 2番目のタッチの位置と平均値を求められるようにする | |
| self.tapLocation = [touch locationWithView:self]; | |
| } | |
| else { | |
| self.twoFingerTapIsPossible = NO; | |
| } | |
| } | |
| // ケース3: これが2つのタッチの2番目の終了である場合 | |
| else if ([touches count] == 1 && allTouchesEnded) { | |
| UITouch *touch = [touches anyObject]; | |
| if ([touch tapCount] == 1) { | |
| // 最後のタッチがシングルタップであれば、これは2本指のタップ | |
| self.tapLocation = midpointBetweenPoints(self.tapLocation, [touch locationInView:self]); | |
| [self handleTwoFingerTap]; | |
| } | |
| } | |
| } | |
| // すべてのタッチが終了した場合、タッチの監視状態をリセットする | |
| if (allTouchesEnded) { | |
| self.twoFingerTapIsPossible = YES; | |
| self.multipleTouches = NO; | |
| } | |
| } | |
| - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event | |
| { | |
| self.twoFingerTapIsPossible = YES; | |
| self.multipleTouches = NO; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ---------------------------------------------- | |
| // タップイベント制御 | |
| // ---------------------------------------------- | |
| - (void)handleSingleTap | |
| { | |
| if ([self.delegate respondsToSelector(@selector(tapDetectingView:goSingleTapAtPoint:)]) { | |
| [self.delegate tapDetectingView:self goSingleTapAtPoint:self.tapLocation]; | |
| } | |
| } | |
| - (void)handleDoubleTap | |
| { | |
| if ([self.delegate respondsToSelector(@selector(tapDetectingView:goDoubleTapAtLocation:)]) { | |
| [self.delegate tapDetectingView:self goDoubleTapAtLocation:self.tapLocation]; | |
| } | |
| } | |
| - (void)handleTwoFingerTap | |
| { | |
| if ([self.delegate respondsToSelector(@selector(tapDetectingView:goTwoFingerTapAtLocation:)]) { | |
| [self.delegate tapDetectingView:self goTwoFingerTapAtPoint:self.tapLocation]; | |
| } | |
| } | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ---------------------------------------------- | |
| // ヘルパー関数 | |
| // ---------------------------------------------- | |
| CGPoint midpointBetweenPoints(CGPoint a, CGPoint b) | |
| { | |
| CGFloat x = (a.x + b.x) / 2.0; | |
| CGFloat y = (a.y + b.y) / 2.0; | |
| return CGPointMake(x, y); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SMPViewController.m | |
| // デリゲートのみ記載 | |
| #define ZOOM_STEP 1.5 | |
| #pragma mark delegate methods | |
| - (void)tapDetectingView:(SMPView *)view goSingleTapAtPoint:(CGPoint)tapPoint | |
| { | |
| // シングルタップは処理しない | |
| } | |
| // ダブルタップ時の処理 | |
| - (void)tapDetectingView:(SMPView *)view goDoubleTapAtPoint:(CGPoint)tapPoint | |
| { | |
| float newScale = self.scrollView.zoomScale / ZOOM_STEP; | |
| CGRect zoomRect = [self zoomRectForScale:newScale withCenter:tapPoint]; | |
| [self.scrollView zoomToRect:zoomRect animated:YES]; | |
| } | |
| // 2本指タップ時の処理 | |
| - (void)tapDetectingView:(SMPView *)view goTwoFingerTapAtPoint:(CGPoint)tapPoint | |
| { | |
| float newScale = self.scrollView.zoomScale / ZOOM_STEP; | |
| CGRect zoomRect = [self zoomRectForScale:newScale withCenter:tapPoint]; | |
| [self.scrollView zoomToRect:zoomRect animated:YES]; | |
| } | |
| #pragma mark helper function | |
| - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center | |
| { | |
| CGRect zoomRect; | |
| // scaleで割ることで、ズーム対象となる矩形のサイズを決める | |
| zoomRect.size.height = self.scrollView.frame.size.height / scale; | |
| zoomRect.size.width = self.scrollView.frame.size.width / scale; | |
| // 矩形の中心をズームの中心にする | |
| zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0); | |
| zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0); | |
| return zoomRect; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment