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筆記中拷貝出來了,所以格式請見諒