標籤:
事件是發送給應用程式來通知它使用者動作的對象。在iOS中,事件可以有多種形式:多觸摸事件,motion(,移動,手 勢)事件---例如,裝置的加速計(accelerometer)--和控制多媒體的事件。(最後一種類型的事件被稱為一個遠端控制事件因為它起始於一個 耳機或其他外部存取配件)。如所示:
在iOS中,UIKit和Core Motion架構負責事件傳播和投遞。
一、事件類型和投遞:
一個iPhone、iPad或者iPod touch裝置有多種方式整合輸入資料流資料給應用程式訪問。多觸摸技術直接操作視圖,包括虛擬鍵盤。三個方向上的加速計。GPS和方向。這些裝置在檢測到觸摸、裝置移動、和定位更改時,處理傳送給系統架構的基礎資料。架構把資料打包並投遞他們到應用程式作為事件來處理。
1,UIKit事件對象和類型:
很多事件都是UIKit架構中的UIEvent的執行個體.UIEvent對象可能封裝了事件的相關狀態,例如關聯的觸摸。它還記錄事件產生的時間。
目前,UIKit可以識別3種類型的事件:觸摸事件、“shaking"(晃動)motion事件,和remote-control事件。UIEvent類聲明了一個enum(枚舉)常量:
typedef enum {
UIEventTypeTouches,
UIEventTypeMotion,
UIEventTypeRemoteControl,
} UIEventType;
typedef enum {
UIEventSubtypeNone = 0,
UIEventSubtypeMotionShake = 1,
UIEventSubtypeRemoteControlPlay = 100,
UIEventSubtypeRemoteControlPause = 101,
UIEventSubtypeRemoteControlStop = 102,
UIEventSubtypeRemoteControlTogglePlayPause = 103,
UIEventSubtypeRemoteControlNextTrack = 104,
UIEventSubtypeRemoteControlPreviousTrack = 105,
UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
UIEventSubtypeRemoteControlEndSeekingBackward = 107,
UIEventSubtypeRemoteControlBeginSeekingForward = 108,
UIEventSubtypeRemoteControlEndSeekingForward = 109,
} UIEventSubtype;
每個事件都有一個事件類型和子類型,通過type和subtype屬性來獲得。觸摸事件的subtype總是UIEventSubtypeNone.
你永遠也不要retain一個UIEvent對象。如果你需要保留事件對象的目前狀態,你應該用適當的方式拷貝和儲存這些狀態,例如用字典儲存這些值。
運行iOS的裝置可以發送另外一些類型的事件,它們不是UIEvent對象,但仍然封裝了一些硬體資訊。
2,事件投遞:
當使用者觸摸裝置時,iOS識別這組觸摸並打包到一個UIEvent對象,並將其放入到活動的應用程式的事件隊列中。如果系統認為裝置的搖動是一個motion event,一個代表該事件的對象也被放入到應用的事件隊列中。單例UIApplication對象從隊列頂部取出一個事件並分發其以處理。一般,它發送事件給應用的關鍵window--使用者事件的焦點視窗。
3,響應者對象和響應者鏈:
二,多觸摸事件:
1,事件和觸摸:一個type屬性為UIEventTypeTouches的UIEvent對象代表一個觸摸事件。當手指觸控螢幕幕並移動於其表面時,系統持續的發送這些觸摸事件對象到一個應用中。事件提供了所有觸摸的快照。
2,觸摸事件的投遞:
1)預設地,一個視圖接收觸摸事件,如果你設定其userInteractionEnabled屬性為NO,那麼會關閉觸摸事件的投遞。一個視圖在隱藏或透明時也不能接收觸摸事件。
2)一個應用可以調用UIApplication的beginIgnoringInteractionEvents並在之後調用endIgnoringInteractionEvents方法。
3)預設地,一個視圖在一個多觸摸序列中,只識別一個手指的觸摸事件。如果你想視圖處理多觸摸,你必須設定mutipleTouchEnabled屬性為YES。
4)預設地,一個視圖的exclusiveTouch屬性為NO,這意味著這個視圖不阻塞其他view接收觸摸。如果你設定這個屬性為YES,如果你正在跟蹤觸摸,那麼只有這個view可以跟蹤觸摸。其他view不能接收這些觸摸資訊。然而,這個視圖也不接收和其他視圖關聯的觸摸資訊。
5)UIView的子類可以重載hitTest:withEvent:來限制多觸摸事件投遞到其子視圖中。
3,處理多觸摸事件:
必須是UIResponder的子類,可以是:UIView、UIViewController、UIImageView、UIApplication或UIWindow。處理事件的方法:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
每個UITouch對象都有一個觸摸階段,例如,touchesBegan:關聯UITouchPhaseBegan,你可以通過UItouch對象的phase屬性來獲得。
4,觸摸事件的基本處理:
通過NSSet對象的anyObject訊息來獲得任意的一個觸摸資訊。
UITouch的一個重要的方法locationInView:。一組相應的方法:previousLocationInView:
tapCount獲得輕擊次數。
allTouches獲得所有的觸摸資訊,因為一個觸摸事件的所有觸摸資訊不一定都在一個View中,所以第一個參數的觸摸NSSet應該是關聯該view的觸摸資訊。如所示:
可以給UIEvent對象發送touchesForWindow:訊息、touchesForView:訊息。
5,處理輕擊手勢:如果你想處理單擊手勢和雙擊手勢,你可以在你的觸摸事件中處理雙擊如下所示:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
if (touch.tapCount >= 2) {
[self.superview bringSubviewToFront:self];
}
}
}
然後添加一個單擊手勢識別。
或者:
1)在觸摸結束後執行 performSelector:withObject:afterDelay:,這個selector代表著單擊手勢要執行的操作。
2)在下一次觸摸開始時 ,檢查tapCount是否是2,如果是,可以 cancelPreviousPerformRequestsWithTarget:方法。如果不是那就識別為單擊事件。如下所示:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *aTouch = [touches anyObject];
if (aTouch.tapCount == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if (theTouch.tapCount == 1) {
NSDictionary *touchLoc = [NSDictionary dictionaryWithObject:
[NSValue valueWithCGPoint:[theTouch locationInView:self]] forKey:@"location"];
[self performSelector:@selector(handleSingleTap:) withObject:touchLoc afterDelay:0.3];
} else if (theTouch.tapCount == 2) {
// Double-tap: increase image size by 10%"
CGRect myFrame = self.frame;
myFrame.size.width += self.frame.size.width * 0.1;
myFrame.size.height += self.frame.size.height * 0.1;
myFrame.origin.x -= (self.frame.origin.x * 0.1) / 2.0;
myFrame.origin.y -= (self.frame.origin.y * 0.1) / 2.0;
[UIView beginAnimations:nil context:NULL];
[self setFrame:myFrame];
[UIView commitAnimations];
}
}
- (void)handleSingleTap:(NSDictionary *)touches {
// Single-tap: decrease image size by 10%"
CGRect myFrame = self.frame;
myFrame.size.width -= self.frame.size.width * 0.1;
myFrame.size.height -= self.frame.size.height * 0.1;
myFrame.origin.x += (self.frame.origin.x * 0.1) / 2.0;
myFrame.origin.y += (self.frame.origin.y * 0.1) / 2.0;
[UIView beginAnimations:nil context:NULL];
[self setFrame:myFrame];
[UIView commitAnimations];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
/* no state to clean up, so null implementation */
}
6,處理輕掃和Drag手勢:
//檢測輕掃
#define HORIZ_SWIPE_DRAG_MIN 12
#define VERT_SWIPE_DRAG_MAX 4
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
// startTouchPosition is an instance variable
startTouchPosition = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self];
// To be a swipe, direction of touch must be horizontal and long enough.
if (fabsf(startTouchPosition.x - currentTouchPosition.x) >= HORIZ_SWIPE_DRAG_MIN &&
fabsf(startTouchPosition.y - currentTouchPosition.y) <= VERT_SWIPE_DRAG_MAX)
{
// It appears to be a swipe.
if (startTouchPosition.x < currentTouchPosition.x)
[self myProcessRightSwipe:touches withEvent:event];
else
[self myProcessLeftSwipe:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
startTouchPosition = CGPointZero;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
startTouchPosition = CGPointZero;
}
//檢測Drag手勢
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *aTouch = [touches anyObject];
CGPoint loc = [aTouch locationInView:self];
CGPoint prevloc = [aTouch previousLocationInView:self];
CGRect myFrame = self.frame;
float deltaX = loc.x - prevloc.x;
float deltaY = loc.y - prevloc.y;
myFrame.origin.x += deltaX;
myFrame.origin.y += deltaY;
[self setFrame:myFrame];
}
7,處理混合多觸摸手勢:
8,Hit-Testing:
9,轉寄觸摸事件:
三、手勢識別者:
1,UIGestureRecognizer是一個抽象類別,具體使用的是其子類:UITapGestureRecognizer、UIPinchGestureRecognizer(縮放)、UIPanGestureRecognizer(Panning or dragging)、UISwipeGestuerRecognizer(任意方向的輕掃)、UIRotationGestureRecognizer(旋轉)、UILongPressGestureRecognizer(長按)
2,手勢被附加到一個view上:對於一些手勢,通過UIGestureRecognizer的locationInView:和locationOfTouch:inView:方法來使得用戶端尋找手勢的位置。
3,手勢觸發動作訊息:
當手勢識別者識別其手勢後,它發送一個或多個動作訊息給一個或多個目標。當你建立一個識別者時,你初始化一個動作和一個目標,你之後可以增加更多的目標-動作對(通過addTarget:action)
4,離散的手勢和持續的手勢:
當識別者識別手勢時,它或者發送一個單獨的動作訊息 或者發送多個動作訊息,直到手勢停止。
5,實現手勢識別:例子:
UITapGestureRecognizer *doubleFingerDTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleDoubleTap:)];
selector函數最後有一個sender參數,可以把UIGestureRecognizer傳進去
建立完UIGestureRecognizer之後,你需要使用view的addGestureRecognizer:, 使用gestureRecognizers屬性讀取,使用removeGestureRecognizer:移除。
doubleFIngerDTap.numberOfTapsRequired=2;
self.theView addGesture ...
6,響應手勢:
離散的手勢略去;持續的手勢,在手勢處理方法中,目標對象經常擷取額外的關於手勢的資訊。例如scale屬性或velocity(速率)屬性。
例子:處理縮放手勢和處理Panning手勢:
- (IBAction)handlePinchGesture:(UIGestureRecognizer *)sender {
CGFloat factor = [(UIPinchGestureRecognizer *)sender scale];
self.view.transform = CGAffineTransformMakeScale(factor, factor);
}
- (IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender {
CGPoint translate = [sender translationInView:self.view];
CGRect newFrame = currentImageFrame;
newFrame.origin.x += translate.x;
newFrame.origin.y += translate.y;
sender.view.frame = newFrame;
if (sender.state == UIGestureRecognizerStateEnded)
currentImageFrame = newFrame;
}
7,與其他的手勢互動:
可能不只一個手勢被添加到一個view上,預設行為中,一個多觸摸序列中的觸摸事件從一個手勢識別者到另一個識別者,並且沒有固定順序,直到事件最終被投遞給view。經常這種預設行為是你想要的。但有時你可能需要另外的行為:
1)讓某個識別者在另外一個識別者可以開始分析觸摸事件之前先失敗。
2)阻止其他識別者分析一個特定的多觸摸序列或那個序列中的一個觸摸對象
3)允許兩個手勢識別者同時操作。
UIGestureRecognizer提供了用戶端方法、代理方法和子類化重載方法來使你更改這些行為。
8,需要一個手勢識別者失敗:
你調用 requireGestureRecognizerToFail:。在發送訊息之後,接收的手勢識別者必須保持UIGestureRecognizerStatusPossible狀態,直到指定的手勢識別者的狀態轉到UIGestureRecognizerStateFailed。如果指定的手勢狀態轉到UIGestureRecognizerStateRecognized或UIGestureRecognizerStateBegan。那麼接收的識別者可以開始,但如果它識別了它的手勢,也沒有動作訊息發送。
9,阻止手勢識別者分析觸摸資訊:
通過代理方法或子類化方法。
UIGestureRecognizerDelegate協議聲明了兩個可選的方法來阻止指定手勢識別者識別手勢on a base-by-base basis:
1) gestureRecognizerShouldBegin: 這個方法在一個識別者嘗試轉變出狀態UIGestureRecognizerStatusPossible時被調用。返回NO使其轉到UIGestureRecognizerStateFailed。預設為YES。
2)gestureRecognizer:shouldReceiveTouch: 這個方法在window對象調用識別者的touchesBegan:withEvent:之前被調用。返回NO表示阻止識別者看到表示觸摸的對象。預設值為YES。
另外,有兩個UIGestureRecognizer的方法影響相同的行為:
-(BOOL)canPreventGestureRecognizer:
-(BOOL)canBePreventedByGestureRecognizer:
10,允許手勢識別同時進行:
預設地,沒有兩個手勢識別者可以同事嘗試識別他們的手勢。但是你可以通過實現代理的可選方法: gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法來更改這個行為。返回YES表示允許兩個手勢識別者同事識別他們的手勢。
注意:返回YES表示允許,返回NO並不表示阻止同時識別因為另外的手勢的代理可能返回YES。
11,事件投遞給Views:
12,影響投遞觸摸到Views的屬性:
你可以更改UIGestureRecognizer的3個屬性來更改預設投遞路徑:
1) cancelsTouchesInView (預設為YES)
2) delaysTouchesBegan (預設為NO)
3)delaysTouchesEnded (預設為YES)
如果你更改這些屬性的值,
1)cancelsTouchesInView設定為NO,導致touchesCancalled:withEvent: 不會被發送給view 任何觸摸--在識別手勢期間。結果是,之前被附加的view在開始或移動階段接收的任何觸摸對象都是無效的。
2)delaysTouchesBegan 設定為YES。 .。。
3) delaysTouchesEnded設定為NO。 .。。
13,建立自己的手勢識別者:
1)狀態轉換:
2)實現一個自訂的手勢識別者:
轉載自: http://supershll.blog.163.com/blog/static/37070436201273113448804/
Event Handling Guide for iOS--事件驅動指南