要想學(xué)好觸摸事件,這第一部分的基礎(chǔ)理論是必須要學(xué)會的,希望大家可以耐心看完。
觸摸事件
是iOS事件中的一種事件類型,在iOS中按照事件劃分還可以分出另外兩類:加速計事件和遠程控制事件,我們現(xiàn)在只學(xué)習(xí)一下觸摸事件,也是iOS中最常用的事件。
注意:我們并不是可以隨意給任何對象添加觸摸事件,只可以給 響應(yīng)者對象
添加,響應(yīng)者對象是指繼承自UIResponder的對象。
所以我們可以給UIView、UIapplication、UIViewController添加,因為它們都是響應(yīng)者對象。
重寫下面幾個系統(tǒng)函數(shù)實現(xiàn)相應(yīng)操作:
一根或者多根手指開始觸摸view,系統(tǒng)會自動調(diào)用view的下面方法- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event一根或者多根手指在view上移動,系統(tǒng)會自動調(diào)用view的下面方法(隨著手指的移動,會持續(xù)調(diào)用該方法)- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event一根或者多根手指離開view,系統(tǒng)會自動調(diào)用view的下面方法- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event觸摸結(jié)束前,某個系統(tǒng)事件(例如電話呼入)會打斷觸摸過程,系統(tǒng)會自動調(diào)用view的下面方法- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event提示:touches中存放的都是UITouch對象
這些函數(shù)里都有NSSet *touches
和UIEvent *event
兩個參數(shù),下面先分別介紹一下,然后再接著學(xué)習(xí)上面幾個函數(shù)的使用
當(dāng)用戶用一根手指觸摸屏幕時,會創(chuàng)建一個與手指相關(guān)聯(lián)的UITouch對象
一根手指對應(yīng)一個UITouch對象
UITouch的作用:
保存著跟手指相關(guān)的信息,比如觸摸的位置、時間、階段
當(dāng)手指移動時,系統(tǒng)會更新同一個UITouch對象,使之能夠一直保存該手指在的觸摸位置
當(dāng)手指離開屏幕時,系統(tǒng)會銷毀相應(yīng)的UITouch對象
UITouch的屬性:
//觸摸產(chǎn)生時所處的窗口@PRoperty(nonatomic,readonly,retain) UIWindow *window;//觸摸產(chǎn)生時所處的視圖@property(nonatomic,readonly,retain) UIView *view;//短時間內(nèi)點按屏幕的次數(shù),可以根據(jù)tapCount判斷單擊、雙擊或更多的點擊@property(nonatomic,readonly) NSUInteger tapCount;//記錄了觸摸事件產(chǎn)生或變化時的時間,單位是秒@property(nonatomic,readonly) NSTimeInterval timestamp;//當(dāng)前觸摸事件所處的狀態(tài)@property(nonatomic,readonly) UITouchPhase phase;
UITouch的方法:
//返回值表示觸摸在view上的位置//調(diào)用時傳入的view參數(shù)為nil的話,返回的是觸摸點在UIWindow的位置- (CGPoint)locationInView:(UIView *)view;
//該方法記錄了前一個觸摸點的位置- (CGPoint)previousLocationInView:(UIView *)view;
UIEvent:稱為事件對象,記錄事件產(chǎn)生的時刻和類型。一個事件,產(chǎn)生一個UIEvent對象。
//事件類型@property(nonatomic,readonly) UIEventType type;@property(nonatomic,readonly) UIEventSubtype subtype;//事件產(chǎn)生的時間@property(nonatomic,readonly) NSTimeInterval timestamp;
一次完整的觸摸過程中,只會產(chǎn)生一個事件對象,4個觸摸方法都是同一個event參數(shù)
如果兩根手指同時觸摸一個view,那么view只會調(diào)用一次touchesBegan:withEvent:方法,touches參數(shù)中裝著2個UITouch對象
如果這兩根手指一前一后分開觸摸同一個view,那么view會分別調(diào)用2次touchesBegan:withEvent:方法,并且每次調(diào)用時的touches參數(shù)中只包含一個UITouch對象
根據(jù)touches中UITouch的個數(shù)可以判斷出是單點觸摸還是多點觸摸
在下面三種情況下,UIView不接收觸摸事件,我們可以利用這一點防止一些不希望出現(xiàn)觸摸的事件發(fā)生。
userInteractionEnabled = NO
隱藏:
hidden = YES
透明
alpha = 0.0 ~ 0.01
提示:UIImageView的userInteractionEnabled默認(rèn)就是NO,因此UIImageView以及它的子控件默認(rèn)是不能接收觸摸事件的。
觸摸事件
的傳遞過程理解這個過程,我們就可以做到攔截系統(tǒng)的事件傳遞,讓我們希望的View層來處理觸摸事件。做到一些特殊的需求。
首先,我們知道,觸摸事件發(fā)生在屏幕上,而在蘋果手機的屏幕之下有很多處理事件的層,依次是:
UIApplication—>UIWindow—>然后是用戶的添加的各種層或控件。
那么,當(dāng)用戶點擊了一個同時在所有層上的點之后,到底哪個層來處理這個事件呢?而這種情況幾乎到處都是。
我們做一個小例子來講解一下:
上圖中假設(shè)是一個手機屏幕,在其上面有很多圖層,數(shù)字代表圖層
的層次,在手機上的觸摸事件過程如下:
這個過程其實就是系統(tǒng)自己在尋找最合適的View來處理這個事件,
如果父控件不能接收觸摸事件,那么子控件就不可能接收到觸摸事件(掌握)
檢查自己是否能接收觸摸事件
監(jiān)察觸摸點是否在自己身上
從·后往前·遍歷子控件,重復(fù)前面的兩個步驟
如果沒有符合條件的子控件,那么就自己最適合處理
系統(tǒng)的實現(xiàn)方法:
系統(tǒng)通過下面的方法實現(xiàn)上面的過程:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
驗證這個過程,我們可以通過重寫這個方法,來驗證這個過程的正確性:
// point:是方法調(diào)用者坐標(biāo)系上的觸摸點的位置- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ // 1.判斷下能否接收觸摸事件 if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.0) return nil; // 2.判斷下點在不在控件上 if ([self pointInside:point withEvent:event] == NO) return nil; // 3.從后往前遍歷子控件 int count = (int)self.subviews.count; for (int i = count - 1; i >= 0 ; i--) { // 取出顯示在最前面的子控件 UIView *childView = self.subviews[i]; // 轉(zhuǎn)換成子控件坐標(biāo)系上點 CGPoint childP = [self convertPoint:point toView:childView]; UIView *fitView = [childView hitTest:childP withEvent:event]; if (fitView) { return fitView; } } // 表示沒有比自己更合適的view return self;}
找到最合適的視圖控件后,就會調(diào)用控件的touches方法來作具體的事件處理
這些touches方法的默認(rèn)做法是將事件順著響應(yīng)者鏈條向上傳遞,將事件交給上一個響應(yīng)者進行處理,這個鏈條的過程一般是下面的兩種
過程如下:
1.
如果view的控制器存在,就傳遞給控制器;如果控制器不存在,則將其傳遞給它的父視圖
2.
在視圖層次結(jié)構(gòu)的最頂級視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進行處理
3.
如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象
4.
如果UIApplication也不能處理該事件或消息,則將其丟棄
為了解決以上的缺點,蘋果推出了手勢識別功能(Gesture Recognizer
)在觸摸事件方面大大簡化了開發(fā)的難度。
為了完成手勢識別,必須借助于手勢識別器----UIGestureRecognizer
UIGestureRecognizer是一個抽象類,定義了所有手勢的基本行為,使用它的子類
才能處理具體的手勢
UITapGestureRecognizer(敲擊)UipinchGestureRecognizer(捏合,用于縮放)UIPanGestureRecognizer(拖拽)UISwipeGestureRecognizer(輕掃)UIRotationGestureRecognizer(旋轉(zhuǎn))UILongPressGestureRecognizer(長按)
每一個手勢識別器的用法都差不多,比如UITapGestureRecognizer的使用步驟如下:
創(chuàng)建手勢識別器對象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
設(shè)置手勢識別器對象的具體屬性
// 連續(xù)敲擊2次tap.numberOfTapsRequired = 2;// 需要2根手指一起敲擊tap.numberOfTouchesRequired = 2;添加手勢識別器到對應(yīng)的view上[self.iconView addGestureRecognizer:tap];監(jiān)聽手勢的觸發(fā)[tap addTarget:self action:@selector(tapIconView:)];
效果圖:
上圖展示了在一個UIImageView上面直接添加多個手勢的例子。彌補了用觸摸方法必須自定義view的缺點。而且實現(xiàn)方法顯得更加簡單。主要代碼如下:
//// ViewController.m// 手勢//// Created by 薛銀亮 on 13/7/11.// Copyright (c) 2013年 薛銀亮. All rights reserved.//#import "ViewController.h"@interface ViewController ()<UIGestureRecognizerDelegate>@property (weak, nonatomic) IBOutlet UIImageView *imageView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]init]; [pinch addTarget:self action:@selector(pinch:)]; pinch.delegate = self; [_imageView addGestureRecognizer:pinch]; UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc]init]; [rotation addTarget:self action:@selector(rotation:)]; rotation.delegate = self; [_imageView addGestureRecognizer:rotation]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init]; [_imageView addGestureRecognizer:tap]; [tap addTarget:self action:@selector(tap:)];}//代理方法,同時實現(xiàn)多個觸摸手勢-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES;}//旋轉(zhuǎn)-(void)rotation:(UIRotationGestureRecognizer *)rotation{ _imageView.transform = CGAffineTransformRotate(_imageView.transform, rotation.rotation); rotation.rotation = 0;}-(void)tap:(UITapGestureRecognizer *)tap{ NSLog(@"ddd");}//捏合手勢-(void)pinch:(UIPinchGestureRecognizer *)pinchGestureRecognizer{ CGFloat scale = pinchGestureRecognizer.scale; _imageView.transform = CGAffineTransformScale(_imageView.transform, scale, scale); pinchGestureRecognizer.scale = 1;}@end
新聞熱點
疑難解答