iOS設(shè)備現(xiàn)如今大受歡迎的最重要原因之一就在于其開創(chuàng)了觸控操作的潮流。發(fā)展到現(xiàn)在,無論是Android還是iPhone,現(xiàn)在APP與用戶進(jìn)行交互,基本上都是依賴于各種各樣的觸控事件。例如用戶對(duì)屏幕進(jìn)行了側(cè)滑,APP就需要對(duì)這個(gè)手勢(shì)進(jìn)行相應(yīng)的處理,給用戶一個(gè)反饋。這些相應(yīng)的事件就都是在UIResponder中定義的。
廣告插播的措不及防:如果您要是覺得這篇文章讓您有點(diǎn)收獲,隨手點(diǎn)個(gè)贊會(huì)讓俺興奮好久吶。
UIResponder大體有四類事件:觸摸、加速計(jì)、遠(yuǎn)程控制、按壓(iOS9.0以后出來的,3DTouch)。

但是在iOS中不是任何對(duì)象都能處理事件,只有繼承了UIResponder的對(duì)象才能接收并處理事件。我們稱之為“響應(yīng)者對(duì)象”。
UIApplication、UIViewController、UIView都繼承自UIResponder,因此它們都是響應(yīng)者對(duì)象,都能夠接收并處理事件。
- UIView繼承自UIResponder,因此所有的控件都是響應(yīng)者對(duì)象
- UIWindow:是特殊的UIView,所以也是響應(yīng)者對(duì)象
- UIApplication,所以也是響應(yīng)者對(duì)象
1. 四類事件的主要方法
有的童鞋可能分不清楚手勢(shì)當(dāng)中結(jié)束和取消的區(qū)別。舉個(gè)栗子,當(dāng)正在撫摸自己的愛機(jī)屏幕的時(shí)候,突然來了一個(gè)電話,這個(gè)“愛撫”的動(dòng)作就被臨時(shí)中斷了,這個(gè)時(shí)候就叫做“取消”,而不是結(jié)束。
1.1 觸摸事件
觸摸事件分成了四部分:開始、移動(dòng)、結(jié)束、取消。
-(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;
1.2 加速計(jì)事件
加速計(jì)事件分成了三部分:開始、結(jié)束、取消。也有人叫做運(yùn)動(dòng)事件,motion events。
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
1.3 遠(yuǎn)程控制事件
收到控制事件:
-(void)remoteControlReceivedWithEvent:(UIEvent *)event;
1.4 按壓事件
按壓事件分成四部分:按壓開始、按壓改變、按壓結(jié)束、按壓取消。
-(void)pressesBegan:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
-(void)pressesChanged:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
-(void)pressesEnded:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
-(void)pressesCancelled:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
2. 響應(yīng)者鏈
概念吶,我們就不說了,網(wǎng)上的文章應(yīng)該一搜一大堆。這里是比較理論化的知識(shí),是比較考驗(yàn)我們對(duì)于iOS中觸摸事件的理解深度的。這里我就只是用簡單的方式,寫一下自己對(duì)于這部分的理解。
根據(jù)第一部分的內(nèi)容,知道UIResponder有好多好多。用戶點(diǎn)擊屏幕之后,系統(tǒng)到底讓誰來響應(yīng)這個(gè)觸摸事件吶?例如用戶點(diǎn)了一個(gè)button,是應(yīng)該讓誰來處理呢?
UIButton肯定是放在一個(gè)UIView上面,UIView也肯定是放在一個(gè)Controller里面。這幾個(gè)都是響應(yīng)者對(duì)象,總不能讓大家一起給用戶反饋吧。系統(tǒng)這里的原則其實(shí)是越具體越好,也就是干活的肯定是最小的那個(gè)小兵兵。要是什么事情都讓UIApplication或者UIWindow干,還不讓它兩兒累死啊,那系統(tǒng)效率要低成神馬樣子。
最終找到這個(gè)干活的控件,我們學(xué)術(shù)上就叫做第一響應(yīng)者對(duì)象。找到了負(fù)責(zé)處理的按鈕之后如何給出相應(yīng)處理呢?大概過程就是這樣:
- button嘗試處理事件。如果它不能處理事件,則將事件傳遞給其父視圖。
- button的父視圖(superview)嘗試處理事件。如果這個(gè)父視圖還不能處理事件,則繼續(xù)將視圖繼續(xù)往上級(jí)傳。
- 上層視圖(topmost view)會(huì)嘗試處理事件。如果這個(gè)上層視圖還是不能處理事件,則將事件傳遞給視圖所在的視圖控制器。
- 視圖控制器會(huì)嘗試處理事件。如果這個(gè)視圖控制器不能處理事件,則將事件傳遞給窗口(window)對(duì)象。
- 窗口(window)對(duì)象嘗試處理事件。如果不能處理,則將事件傳遞給UIApplication。
- 如果UIApplication不能處理事件,則丟棄這個(gè)事件。就是白按嘍。
一次完整的觸摸事件的傳遞響應(yīng)的過程大概是這樣的: UIAppliction --> UIWindow -->遞歸找到最適合處理事件的控件-->控件調(diào)用touches方法-->判斷是否實(shí)現(xiàn)touches方法-->沒有實(shí)現(xiàn)默認(rèn)會(huì)將事件傳遞給上一個(gè)響應(yīng)者-->找到上一個(gè)響應(yīng)者。
對(duì)于第一響應(yīng)者,UIResponder提供了一系列方法,我們分別來介紹一下。
- 如果想判定一個(gè)響應(yīng)對(duì)象是否是第一響應(yīng)者,則可以使用以下方法:
- (BOOL)isFirstResponder
- 如果我們希望將一個(gè)響應(yīng)對(duì)象作為第一響應(yīng)者,則可以使用以下方法:
- (BOOL)becomeFirstResponder
- 一個(gè)響應(yīng)對(duì)象只有在當(dāng)前響應(yīng)者能放棄第一響應(yīng)者狀態(tài)(
canResignFirstResponder)且自身能成為第一響應(yīng)者(canBecomeFirstResponder)時(shí)才會(huì)成為第一響應(yīng)者。換種說法就是,有人想當(dāng)部門老大,那也得現(xiàn)在的部門頭愿意放權(quán),并且這個(gè)人還有能力才能成為老大。
//判斷是否能夠成為第一響應(yīng)者
- (BOOL)canBecomeFirstResponder
//響應(yīng)者放棄第一響應(yīng)者身份
- (BOOL)resignFirstResponder
- (BOOL)canResignFirstResponder
這些方法大家用的都會(huì)比較多,特別是想讓文本輸入框獲取到焦點(diǎn)的時(shí)候。
3. 手勢(shì)識(shí)別功能(Gesture Recognizer)
- 如果想監(jiān)聽一個(gè)view上面的觸摸事件,之前的做法是
- 自定義一個(gè)view
- 實(shí)現(xiàn)view的touches方法,在方法內(nèi)部實(shí)現(xiàn)具體處理代碼
- 通過touches方法監(jiān)聽view觸摸事件,有很明顯的幾個(gè)缺點(diǎn)
- 必須得自定義view
- 由于是在view內(nèi)部的touches方法中監(jiān)聽觸摸事件,因此默認(rèn)情況下,無法讓其他外界對(duì)象監(jiān)聽view的觸摸事件
- 不容易區(qū)分用戶的具體手勢(shì)行為- iOS 3.2之后,蘋果推出了手勢(shì)識(shí)別功能(Gesture Recognizer),在觸摸事件處理方面,大大簡化了開發(fā)者的開發(fā)難度
3.1手勢(shì)識(shí)別器(UIGestureRecognizer)
- 為了完成手勢(shì)識(shí)別,必須借助于手勢(shì)識(shí)別器——UIGestureRecognizer
- 利用UIGestureRecognizer,能輕松識(shí)別用戶在某個(gè)view上面做的一些常見手勢(shì)
- UIGestureRecognizer是一個(gè)抽象類,定義了所有手勢(shì)的基本行為,使用它的子類才能處理具體的手勢(shì)
- UITapGestureRecognizer(點(diǎn)按)
- UIPinchGestureRecognizer(捏合,用于縮放)
- UIPanGestureRecognizer(拖動(dòng))
- UISwipeGestureRecognizer(輕掃)
- UIRotationGestureRecognizer(旋轉(zhuǎn))
- UILongPressGestureRecognizer(長按)
3.2 手勢(shì)識(shí)別的使用方法
- 1.創(chuàng)建手勢(shì)識(shí)別實(shí)例
- 2.設(shè)置手勢(shì)識(shí)別屬性,例如手指數(shù)量,方向等
- 3.將手勢(shì)識(shí)別附加到指定的視圖之上
- 4.編寫手勢(shì)觸發(fā)監(jiān)聽方法
- 每一個(gè)手勢(shì)識(shí)別器的用法都差不多,比如UITapGestureRecognizer的使用步驟如下:
//創(chuàng)建手勢(shì)識(shí)別器對(duì)象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
//設(shè)置手勢(shì)識(shí)別器對(duì)象的具體屬性,例如連續(xù)敲擊2次
tap.numberOfTapsRequired = 2;
// 需要2根手指一起敲擊
tap.numberOfTouchesRequired = 2;
//添加手勢(shì)識(shí)別器到對(duì)應(yīng)的view上
[self.iconView addGestureRecognizer:tap];
//監(jiān)聽手勢(shì)的觸發(fā)
[tap addTarget:self action:@selector(tapIconView:)];
3.3手勢(shì)識(shí)別的枚舉
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// 沒有觸摸事件發(fā)生,所有手勢(shì)識(shí)別的默認(rèn)狀態(tài)
UIGestureRecognizerStatePossible,
// 一個(gè)手勢(shì)已經(jīng)開始但尚未改變或者完成時(shí)
UIGestureRecognizerStateBegan, (類似于 touchesBegan)
// 手勢(shì)狀態(tài)改變
UIGestureRecognizerStateChanged, (類似于 touchesMoved)
// 手勢(shì)完成
UIGestureRecognizerStateEnded, (類似于 touchesEnded)
// 手勢(shì)取消,恢復(fù)至Possible狀態(tài)
UIGestureRecognizerStateCancelled, (比如手指按下按鈕,然后從其他地方抬起)
// 手勢(shì)失敗,恢復(fù)至Possible狀態(tài)
UIGestureRecognizerStateFailed,
// 識(shí)別到手勢(shì)識(shí)別
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};
4. 手勢(shì)的使用
4.1 長按手勢(shì)
- 長按手勢(shì)一定要判斷狀態(tài),否則方法會(huì)在手勢(shì)開始和結(jié)束時(shí)分別調(diào)用!方法會(huì)被調(diào)用兩次!
- (void)addLongPressGesture
{
//創(chuàng)建長按手勢(shì)識(shí)別并添加監(jiān)聽事件
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
// 給圖片添加長按手勢(shì)
[self.imageView addGestureRecognizer:longPress];
}
//識(shí)別到長按手勢(shì)后回調(diào)的方法
- (void)longPress:(UILongPressGestureRecognizer *)recognizer
{
// 判斷手勢(shì)的狀態(tài),長按手勢(shì)一定要判斷狀態(tài),否則方法會(huì)在手勢(shì)開始和結(jié)束時(shí)分別調(diào)用!方法會(huì)被調(diào)用兩次!
if (recognizer.state == UIGestureRecognizerStateBegan) {
//動(dòng)畫 改變圖片的透明度
[UIView animateWithDuration:0.5 animations:^{
self.imageView.alpha = 0.5;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 animations:^{
self.imageView.alpha = 1.0;
}];
}];
}
}
4.2 清掃手勢(shì)
- 如果要監(jiān)聽多個(gè)輕掃方向,需要添加多個(gè)輕掃手勢(shì)
- 輕掃手勢(shì)默認(rèn)支持向右的掃動(dòng)方向
- 因?yàn)檩p掃手勢(shì)要求用戶比較放松的掃動(dòng),因此最好不要將此手勢(shì)添加到某一個(gè)視圖上,會(huì)局限用戶的操作
- (void)addSwipeGesture
{
// 如果要監(jiān)聽多個(gè)輕掃方向,需要添加多個(gè)輕掃手勢(shì)
// 輕掃手勢(shì)默認(rèn)支持向右的掃動(dòng)方向
//創(chuàng)建輕掃手勢(shì)識(shí)別并添加監(jiān)聽事件(默認(rèn)是向右掃動(dòng))
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
//創(chuàng)建輕掃手勢(shì)識(shí)別并添加監(jiān)聽事件
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
//direction 方向 向左輕掃
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
/**
UISwipeGestureRecognizerDirectionRight 向右輕掃(默認(rèn))不設(shè)輕掃方向 就是向右
UISwipeGestureRecognizerDirectionLeft 向左輕掃
UISwipeGestureRecognizerDirectionUp 向上輕掃
UISwipeGestureRecognizerDirectionDown 向下輕掃
*/
// 因?yàn)檩p掃手勢(shì)要求用戶比較放松的掃動(dòng),因此最好不要將此手勢(shì)添加到某一個(gè)視圖上,會(huì)局限用戶的操作
// 添加手勢(shì)
[self.view addGestureRecognizer:swipe];
[self.view addGestureRecognizer:swipeLeft];
}
//識(shí)別到輕掃手勢(shì)后回調(diào)的方法
- (void)swipe:(UISwipeGestureRecognizer *)recognizer
{
//當(dāng)前獲取中心位置
CGPoint from = self.imageView.center;
//目標(biāo)
CGPoint to;
//向左輕掃
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft) {
to = CGPointMake(-from.x, from.y);
} else {//向右輕掃
to = CGPointMake(3 * from.x, from.y);
}
//動(dòng)畫移動(dòng)圖片
[UIView animateWithDuration:0.5 animations:^{
self.imageView.center = to;
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 animations:^{
self.imageView.center = from;
}];
}];
}
4.3 拖動(dòng)手勢(shì)
- (void)addPanGesture
{
//創(chuàng)建拖動(dòng)手勢(shì) 并添加手勢(shì)的監(jiān)聽事件
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
//添加手勢(shì)
[self.imageView addGestureRecognizer:pan];
}
//識(shí)別到拖動(dòng)手勢(shì)后回調(diào)的方法
- (void)pan:(UIPanGestureRecognizer *)recognizer
{
//獲取手指按在圖片上的位置 以圖片左上角為原點(diǎn)
CGPoint translation = [recognizer translationInView:self.imageView];
// 移動(dòng)圖片
recognizer.view.transform = CGAffineTransformTranslate(recognizer.view.transform, translation.x, translation.y);
//給平移復(fù)位 因?yàn)樗窃谠谢A(chǔ)上當(dāng)前遞增平移 如果不復(fù)位 或清空他會(huì)越變?cè)酱? [recognizer setTranslation:CGPointZero inView:self.imageView];
}
4.4 捏合手勢(shì)
- (void)addPinchGesture
{
//創(chuàng)建縮放(捏合)手勢(shì) 并添加手勢(shì)的監(jiān)聽事件
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
//設(shè)置控制器為縮放手勢(shì)的代理 可以實(shí)現(xiàn)同時(shí)識(shí)別兩個(gè)手勢(shì)
pinch.delegate = self;
[self.imageView addGestureRecognizer:pinch];
}
//識(shí)別到 縮放(捏合)手勢(shì)后回調(diào)的方法
- (void)pinch:(UIPinchGestureRecognizer *)recognizer
{
//綻放圖片
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
//將縮放比例復(fù)位
recognizer.scale = 1.0;
}
4.5 旋轉(zhuǎn)手勢(shì)
- (void)addRotateGesture
{
//創(chuàng)建縮放 旋轉(zhuǎn)并添加手勢(shì)的監(jiān)聽事件
UIRotationGestureRecognizer *rotate = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotate:)];
//設(shè)置控制器為縮放手勢(shì)的代理 可以實(shí)現(xiàn)同時(shí)識(shí)別兩個(gè)手勢(shì)
rotate.delegate = self;
// 添加手勢(shì)
[self.imageView addGestureRecognizer:rotate];
}
//識(shí)別到旋轉(zhuǎn)手勢(shì)后的回調(diào)方法
- (void)rotate:(UIRotationGestureRecognizer *)recognizer
{
//圖片旋轉(zhuǎn)
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
//將手勢(shì)識(shí)別的旋轉(zhuǎn)角度復(fù)位
recognizer.rotation = 0.0; //非常重要 角度也會(huì)疊加
}
4.6 單擊手勢(shì)
- (void)addTapGesture
{
//創(chuàng)建縮放點(diǎn)按(單擊,點(diǎn)擊)并添加手勢(shì)的監(jiān)聽事件
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
// 添加手勢(shì)
[self.imageView addGestureRecognizer:tapGesture];
}
//識(shí)別到手勢(shì)后的回調(diào)方法
- (void)tap
{
NSLog(@"點(diǎn)我了");
}
4.7 手勢(shì)的總結(jié)
- 一定記住設(shè)置完transform之后,需要將對(duì)應(yīng)的形變參數(shù)復(fù)位
- 手勢(shì)識(shí)別,是單獨(dú)添加到某一個(gè)視圖上的
- 如果要同時(shí)支持多個(gè)手勢(shì)識(shí)別,需要設(shè)置手勢(shì)識(shí)別的代理
是否支持多手勢(shì)觸摸的代理方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
謝謝您看完這么長一篇文章,辛苦啦~!點(diǎn)個(gè)贊吧。哈哈??