13.1 事件概述
13.2 觸摸事件
13.3 手勢
13.1 事件概述
事件是當用戶手指觸擊屏幕及在屏幕上移動時,系統不斷發送給應用程序的對象。
系統將事件按照特定的路徑傳遞給可以對其進行處理的對象。
在iOS中,一個UITouch對象表示一個觸摸,一個UIEvent對象表示一個事件。事件對象中包含與當前多點觸摸序列相對應的所有觸摸對象,還可以提供與特定視圖或窗口相關聯的觸摸對象。
響應者對象
響應者對象是可以響應事件並對其進行處理的對象。
UIResponder是所有響應者對象的基類,它不僅為事件處理,而且也為常見的響應者行為定義編程接口。
UIApplication、UIView、和所有從UIView派生出來的UIKit類(包括UIWindow)都直接或間接地繼承自UIResponder類。
第一響應者是應用程序中當前負責接收觸摸事件的響應者對象(通常是一個UIView對象)。UIWindow對象以消息的形式將事件發送給第一響應者,使其有機會首先處理事件。如果第一響應者沒有進行處理,系統就將事件(通過消息)傳遞給響應者鏈中的下一個響應者,看看它是否可以進行處理。
響應者鏈
響應鏈是一個響應者對象的連接序列,事件或動作消息(或菜單編輯消息)依次傳遞。它允許響應者對象把事件處理的職責轉交給其它更高層的對象。應用程序通過向上傳遞一個事件來查找合適的處理對象。因為點擊檢測視圖也是一個響應者對象,應用程序在處理觸摸事件時也可以利用響應鏈。響應鏈由一系列的下一個響應者組成。
響應者鏈處理原則
1. 點擊檢測視圖或者第一響應者傳遞事件或動作消息給它的視圖控制器(如果它有的話);如果沒有一個視圖控制器,就傳遞給它的父視圖。
2. 如果一個視圖或者它的視圖控制器不能處理這個事件或動作消息,它將傳遞給該視圖的父視圖。
3. 在這個視圖層次中的每個后續的父視圖遵循上述的模式,如果它不能處理這個事件或動作消息的話。
4. 最頂層的視圖如果不能處理這個事件或動作消息,就傳遞給UIWindow對象來處理。
5. 如果UIWindow 對象不能處理,就傳給單件應用程序對象UIApplication。
如果應用程序對象也不能處理這個事件或動作消息,將拋棄它。
13.2 觸摸事件
觸摸信息有時間和空間兩方面,時間方面的信息稱為階段(phrase),表示觸摸是否剛剛開始、是否正在移動或處於靜止狀態,以及何時結束—也就是手指何時從屏幕抬起。觸摸信息還包括當前在視圖或窗口中的位置信息,以及之前的位置信息(如果有的話)。當一個手指接觸屏幕時,觸摸就和某個窗口或視圖關聯在一起,這個關聯在事件的整個生命周期都會得到維護。
觸摸事件的階段
事件處理方法
在給定的觸摸階段中,如果發生新的觸摸動作或已有的觸摸動作發生變化,應用程序就會發送這些消息:
當一個或多個手指觸碰屏幕時,發送touchesBegan:withEvent:消息。
當一個或多個手指在屏幕上移動時,發送touchesMoved:withEvent:消息。
當一個或多個手指離開屏幕時,發送touchesEnded:withEvent:消息。
當觸摸序列被諸如電話呼入這樣的系統事件所取消時,發送touchesCancelled:withEvent:消息。
觸摸事件實例 EventInfo
#import <UIKit/UIKit.h> @interface TouchView : UIView { } - (void)logTouchInfo:(UITouch *)touch; @end
@implementation TouchView - (void)logTouchInfo:(UITouch *)touch { CGPoint locInSelf = [touch locationInView:self]; CGPoint locInWin = [touch locationInView:nil]; NSLog(@" touch.locationInView = {%2.3f, %2.3f}", locInSelf.x, locInSelf.y); NSLog(@" touch.locationInWin = {%2.3f, %2.3f}", locInWin.x, locInWin.y); NSLog(@" touch.phase = %d", touch.phase); NSLog(@" touch.tapCount = %d", touch.tapCount); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesBegan - touch count = %d", [touches count]); for(UITouch *touch in event.allTouches) { [self logTouchInfo:touch]; } }
touch.phase,觸摸事件的階段。
touch.tapCount,觸摸事件的輕碰次數,可以判斷雙擊事件。
UIEvent 的allTouches方法,可以獲得觸摸點的集合,可以判斷多點觸摸事件。
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesMoved - touch count = %d", [touches count]); for(UITouch *touch in event.allTouches) { [self logTouchInfo:touch]; } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesEnded - touch count = %d", [touches count]); for(UITouch *touch in event.allTouches) { [self logTouchInfo:touch]; } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesCancelled - touch count = %d", [touches count]); for(UITouch *touch in event.allTouches) { [self logTouchInfo:touch]; } }
13.3 手勢
手勢在iPhone中很重要,手勢就是手觸摸屏幕的方式。
單碰擊
雙碰擊
多點觸摸(合攏和展開)
輕撫
… …
單碰擊和雙碰擊實例:MultiTap
單碰擊為紅色,雙碰擊為藍色
#import <UIKit/UIKit.h> @interface MultiTapView : UIView { } @end
#import "MultiTapView.h" @implementation MultiTapView - (void)turnBlue { self.backgroundColor = [UIColor blueColor]; } - (void)turnRed { self.backgroundColor = [UIColor redColor]; } //START:code.MultiTapView.touchesBegan: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; if(touch.tapCount == 2) { [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(turnRed) object:nil]; } } //END:code.MultiTapView.touchesBegan: //START:code.MultiTapView.touchesEnded: - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; if(touch.tapCount == 1) { [self performSelector:@selector(turnRed) withObject:nil afterDelay:0.10f]; } if(touch.tapCount == 2) { [self turnBlue]; } } //END:code.MultiTapView.touchesEnded: @end
[self performSelector:@selector(turnRed) withObject:nil afterDelay:0.10f]; 是在0.1秒后調用turnRed方法。
[[self class]cancelPreviousPerformRequestsWithTarget:self selector:@selector(turnRed) object:nil]; 是取消調用方法turnRed。
多點觸摸(合攏和展開)PinchZoom
PinchZoomView .h文件
#import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> @interface PinchZoomView : UIView { CALayer *robotLayer; CGFloat previousDistance; CGFloat zoomFactor; BOOL pinchZoom; } @property(nonatomic, retain) CALayer *robotLayer; @end
m文件
#import "PinchZoomView.h" @implementation PinchZoomView @synthesize robotLayer; - (void)awakeFromNib { self.robotLayer = [CALayer layer]; UIImage *image = [UIImage imageNamed:@"Robot.png"]; self.robotLayer.contents = (id)[image CGImage]; self.robotLayer.bounds = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height); self.robotLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); [self.layer addSublayer:self.robotLayer]; pinchZoom = NO; previousDistance = 0.0f; zoomFactor = 1.0f; }
awakeFromNib當nib文件被加載的時候,加載器會發送一個awakeFromNib的消息到nib文件中的每個對象,每個對象都可以定義自己的 awakeFromNib方法來響應這個消息,執行一些必要的操作。也就是說通過nib文件創建view對象是執行awakeFromNib 。
robotLayer是 CALayer 對象,本例子中我們把圖片對象添加到robotLayer對象中。使用 CALayer需要引入 <QuartzCore/QuartzCore.h>頭文件和添加QuartzCore.framework框架。
//START:code.PinchZoomView.touchesBegan - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if(event.allTouches.count == 2) { pinchZoom = YES; NSArray *touches = [event.allTouches allObjects]; CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self]; CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self]; previousDistance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) + pow(pointOne.y - pointTwo.y, 2.0f)); } else { pinchZoom = NO; } } //END:code.PinchZoomView.touchesBegan
previousDistance 是獲得兩個點的距離。
pow是平方函數。
sqrt是開平方根函數。
//START:code.PinchZoomView.touchesMoved - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { if(YES == pinchZoom && event.allTouches.count == 2) { NSArray *touches = [event.allTouches allObjects]; CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self]; CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self]; CGFloat distance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) + pow(pointOne.y - pointTwo.y, 2.0f)); zoomFactor += (distance - previousDistance) / previousDistance; zoomFactor = fabs(zoomFactor); previousDistance = distance; self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f); } } //END:code.PinchZoomView.touchesMoved
//START:code.PinchZoomView.touchesEnded - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if(event.allTouches.count != 2) { pinchZoom = NO; previousDistance = 0.0f; } if(event.allTouches.count == 1) { // NSArray *touches = [event.allTouches allObjects]; // UITouch *touch = [touches objectAtIndex:0]; UITouch *touch = [touches anyObject]; NSInteger tapCount = [touch tapCount]; if (tapCount == 2) { zoomFactor += 0.4; self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f); } else if (tapCount == 3) { zoomFactor += 0.6; self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f); } else if (tapCount == 4) { zoomFactor += 0.8; self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f); } } } //END:code.PinchZoomView.touchesEnded - (void)dealloc { self.robotLayer = nil; [robotLayer release]; [super dealloc]; }
注:
1 本教程是基於關東升老師的教程
2 基於黑蘋果10.6.8和xcode4.2
3 本人初學,有什么不對的望指教
4 教程會隨着本人學習,持續更新
5 教程是本人從word筆記中拷貝出來了,所以格式請見諒