UITouch的解釋及作用
- 一根手指對應(yīng)一個UITouch對象
- 當(dāng)用戶用一根手指觸摸屏幕時,會創(chuàng)建一個與手指相關(guān)聯(lián)的UITouch對象
- 保存著跟手指相關(guān)的信息,比如觸摸的位置、時間、階段
- 當(dāng)手指移動時,系統(tǒng)會更新同一個UITouch對象,使之能夠一直保存該手指在的觸摸位置
- 當(dāng)手指離開屏幕時,系統(tǒng)會銷毀相應(yīng)的UITouch對象
UITouch的方法
- (CGPoint)locationInView:(UIView *)view;
返回值表示觸摸在view上的位置
這里返回的位置是針對view的坐標(biāo)系的(以view的左上角為原點(0, 0))
調(diào)用時傳入的view參數(shù)為nil的話,返回的是觸摸點在UIWindow的位置
- (CGPoint)previousLocationInView:(UIView *)view;
該方法記錄了前一個觸摸點的位置
UITouch的屬性
觸摸產(chǎn)生時所處的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
觸摸產(chǎn)生時所處的視圖
@property(nonatomic,readonly,retain) UIView *view;
UIEvent的解釋及作用
每產(chǎn)生一個事件,就會產(chǎn)生一個UIEvent對象
UIEvent:稱為事件對象,記錄事件產(chǎn)生的時刻和類型
UIEvent還提供了相應(yīng)的方法可以獲得在某個view上面的觸摸對象(UITouch)
UIEvent的屬性
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
@property(nonatomic,readonly) NSTimeInterval timestamp;
touches和event參數(shù)
一次完整的觸摸過程,會經(jīng)歷3個狀態(tài):
觸摸開始:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
觸摸移動:- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
觸摸結(jié)束:- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
觸摸取消(可能會經(jīng)歷):- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
4個觸摸事件處理方法中,都有NSSet *touches和UIEvent *event兩個參數(shù)
一次完整的觸摸過程中,只會產(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ù)可以判斷出是單點觸摸還是多點觸摸
事件的產(chǎn)生和傳遞
發(fā)生觸摸事件后,系統(tǒng)會將該事件加入到一個由UIApplication管理的事件隊列中
-
UIApplication會從事件隊列中取出最前面的事件,并將事件分發(fā)下去以便處理,通常,先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)
主窗口會在視圖層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理過程的第一步
找到合適的視圖控件后,就會調(diào)用視圖控件的touches方法( touchesBegan…
touchesMoved…
touchedEnded…)來作具體的事件處理
觸摸事件處理的詳細(xì)過程
用戶點擊屏幕后產(chǎn)生的一個觸摸事件,經(jīng)過一系列的傳遞過程后,會找到最合適的視圖控件來處理這個事件
找到最合適的視圖控件后,就會調(diào)用控件的touches方法來作具體的事件處理(touchesBegan…,touchesMoved…,touchedEnded…)
touchesBegan方法的默認(rèn)做法是將事件順著響應(yīng)者鏈條向上傳遞,將事件交給上一個響應(yīng)者進(jìn)行處理
響應(yīng)者對象
- 在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接收并處理事件。我們稱之為“響應(yīng)者對象”(注意:之前說的類對象其實就是類,例如UIImageView類對象,其實就是UIImageView類,而不是UIImageView類創(chuàng)建出來的對象)
- UIApplication、UIViewController、UIView都繼承自UIResponder,因此它們都是響應(yīng)者對象,都能夠接收并處理事件
UIResponder內(nèi)部提供了以下方法來處理事件.因為UIApplication、UIViewController、UIView都繼承UIResponder,所以這三個都能擁有以下方法(事件)
觸摸事件
- (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;
加速計事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
UIView不接收觸摸事件的三種情況
- 1:userInteractionEnabled = NO(不接收用戶交互)
- 2:hidden = YES(隱藏)
- 3:alpha = 0.0 ~ 0.01(透明)
- 提示:UIImageView的userInteractionEnabled默認(rèn)就是NO,因此UIImageView以及它的子控件默認(rèn)是不能接收觸摸事件的
觸摸事件的傳遞
- 觸摸事件的傳遞是從父控件傳遞到子控件(最里面的就是父控件,最外面的就是子控件)
- 點擊了綠色的view,事件傳遞的過程:UIApplication -> UIWindow -> 白色 -> 綠色
- 點擊了藍(lán)色的view,事件傳遞的過程:UIApplication -> UIWindow -> 白色 -> 橙色 -> 藍(lán)色
- 點擊了黃色的view,事件傳遞的過程:UIApplication -> UIWindow -> 白色 -> 橙色 -> 藍(lán)色 -> 黃色
- 觸摸事件的傳遞過程中,如果父控件不能接收觸摸事件(上面的1,2,3),那么子控件也不可能接收到觸摸事件(掌握)。
- 這種情況接收到觸摸事件的只有父控件的父控件。例如藍(lán)色userInteractionEnabled = NO,那么子控件黃色就不能接收到觸摸事件,此時點擊黃色區(qū)域,提示橙色被點擊,而不是提示黃色被點擊。
- 詳細(xì)解釋:點擊黃色,觸摸事件的傳遞過程是從父控件到子控件,當(dāng)傳遞到橙色時,橙色要傳遞給藍(lán)色,因為藍(lán)色不接收用戶交互,所以觸摸事件就在經(jīng)過橙色view停止,不再進(jìn)行傳遞。
- 如果父控件隱藏,那么里面的子控件也跟著隱藏
- 如果父控件透明(alpha),那么子控件也跟著透明。不過在storyboard中看不出子控件也跟著透明,運行時才會看到
- 如何找到最合適的控件來處理事件(利用事件的傳遞過程)?
- A:自己是否能接收觸摸事件?
- B:觸摸點是否在自己身上?(觸摸點在子控件身上,若這個子控件有父控件,那么這個觸摸點也在父控件身上)
- C:從后往前遍歷子控件,重復(fù)前面的兩個步驟
- D:如果沒有符合條件的子控件,那么就自己最適合處理
事例+附圖
- 點擊(觸摸)黃色的View,找到最合適的控件來處理事件的詳細(xì)步驟:
- UIApplication從隊列當(dāng)中取出事件
- 事件先傳遞給UIWindow
- UIWindow滿足A,B,此時事件傳遞給控制器的白色View
- 白色的View滿足A,B,此時白色的View把事件傳遞給橙色View(因為白色View子控件是綠色和橙色,在storyboard中,橙色是后添加的,后添加的先遍歷)
- 橙色的View滿足A,B,此時橙色的view把事件傳遞給紅色的View
- 紅色的View滿足A,不滿足B。然后橙色的view把事件傳遞給藍(lán)色的View
- 藍(lán)色的View滿足A,B。藍(lán)色的View把事件傳遞給黃色的View
- 黃色的View滿足A,B,但是沒有子控件,所以C,D不滿足,最終藍(lán)色的View來處理事件,所以執(zhí)行藍(lán)色的View點擊
精華總結(jié)1:
- 判斷點在不在白色的View身上,一定要以白色View的左上角為坐標(biāo)原點,不能以父控件(紅色View)的左上角為原點判斷,
- 判斷依據(jù):判斷點A(x,y)是否大于當(dāng)前View(白色View)的寬度和高度,如果有一個不滿足,則點不在當(dāng)前的View上。例如如果A的坐標(biāo)為(15,4),而白色View的寬高都為28,所以滿足條件,可得知A點在白色View上
- 如果以紅色View的左上角為坐標(biāo)原點,判斷當(dāng)前點A是在白色View上的話,A點的坐標(biāo)為(50,30),而白色的View的寬高都為28,所以不滿足條件,由此判斷A點不在白色View。
-判斷點在不在白色View身上的官方語言是判斷當(dāng)前點在不在方法調(diào)用者身上。就是說,當(dāng)前方法的調(diào)用者:白色View - 精華:hitTest方法內(nèi)部實現(xiàn)了A,B,C,D(詳細(xì)看下面的代碼)。 所以說方法的調(diào)用者就是 執(zhí)行白色View的hitTest方法
精華總結(jié)2(結(jié)合總結(jié)1):
事件傳遞到紅色View時,執(zhí)行紅色view的hitTest方法,hitTest方法里面的A,B,C,執(zhí)行到C,從后往前遍歷子控件,子控件就是白色View,執(zhí)行白色View(截圖16-06)的hitTest方法,參數(shù)中的point是父控件紅色View的傳遞過來的坐標(biāo),所以傳遞過來的point要轉(zhuǎn)換成當(dāng)前方法所在的View的piont
轉(zhuǎn)換規(guī)則:當(dāng)前點的位置不變,變得只是坐標(biāo)原點(父控件的坐標(biāo)原點轉(zhuǎn)成了子控件的坐標(biāo)原點。)然后計算點的位置距離這個子控件的x,y坐標(biāo),這個坐標(biāo)就是這個點在子控件的point,如果此時point大于這個子控件的寬和高,那么point不在這個子控件上
代碼方式轉(zhuǎn)換:如16-06代碼截圖(結(jié)合 總結(jié)1,總結(jié)2)
- 詳細(xì)圖解 (結(jié)合 總結(jié)1,總結(jié)2)
HitTest方法底層實現(xiàn)
- 精華:hitTest方法內(nèi)部實現(xiàn)了A,B,C,D
//什么時候調(diào)用:當(dāng)一個事件傳遞給當(dāng)前View時調(diào)用
//作用:尋找最適合的View
//返回值:找到的最適合的View,如果沒有找到最適合的View,返回是一個nil.
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//1.判斷當(dāng)前View能否接收事件
if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
return nil;
}
//2.判斷當(dāng)前點在不在View身上
if (![self pointInside:point withEvent:event]) {
return nil;
}
//3.從后往前遍歷自己的子控件.讓自己的子控件尋找最適合的View;
int count = (int)self.subviews.count;
for (int i = count - 1; i >= 0; i--) {
//取出子控件
UIView *childV = self.subviews[i];
//把當(dāng)前點的坐標(biāo)(父控件)系轉(zhuǎn)換成子控件身上的坐標(biāo)系
CGPoint childP = [self convertPoint:point toView:childV];
UIView *fitView = [childV hitTest:childP withEvent:event];
if (fitView) {
return fitView;
}
}
//4.沒有找到比自己更適合的view,那么它自己就是最適合的view
return self;
}
//什么時候調(diào)用:在hitTest方法當(dāng)中調(diào)用
//作用:判斷當(dāng)前點在不在方法調(diào)用者身上.
//返回值:如果返回yes,在View身,如果 為NO,不在View身上.
//傳入的點point,必須得與方法調(diào)用者在同一個坐標(biāo)系(即point是父控件中的坐標(biāo),必須轉(zhuǎn)換成子控件身上的坐標(biāo),即坐標(biāo)原點要由父控件變成子控件的坐標(biāo)原點,唯一不變的是坐標(biāo)代表的距離不變,這個距離要和當(dāng)前子控件的寬高進(jìn)行比較,如果都小于的話,才能證明這個帶你在子控件身上).
//- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// return YES;
//}
@end
hitTest方法練習(xí)
CharBtn.h文件
#import <UIKit/UIKit.h>
@interface ChatBtn : UIButton
/** <#注釋#>*/
@property (nonatomic, weak) UIButton *popBtn;
@end
CharBtn.m文件
#import "ChatBtn.h"
@implementation ChatBtn
//當(dāng)?shù)谝淮吸c擊"彈出對話框"時,按照事件的傳遞過程:APPLication--->UIWindow--->"彈出對話框"按鈕,執(zhí)行到"彈出對話框"按鈕時候,所以執(zhí)行"彈出對話框"重寫的hitTest方法(重寫只為攔截之用,但還是會執(zhí)行btnclick方法),在這個方法里面進(jìn)行判斷(A,B,C,D步驟),結(jié)果返回系統(tǒng)的做法return [super hitTest:point withEvent:event];所以跳出hitTest方法之后,就會執(zhí)行btnclick方法.在里面創(chuàng)建一個btn控件并添加到父控件"彈出對話框",并把這個控件賦值給ChatBtn類型的對象sender的popBtn屬性,從而popBtn就拿到了btn,擁有了btn的圖片以及尺寸存儲在內(nèi)存中
//第一次點擊"彈出對話框"時打印兩行null的原因: 第一行:因為沒有給self.popBtn賦值,所以self.popBtn為null.第二行:self.popBtn為null,返回的是一個方法,所以再次執(zhí)行這個重寫的方法就返回了,打印第二行null。
//第二次點擊"對話框",按照事件的傳遞過程:APPLication--->UIWindow--->"彈出對話框"按鈕,執(zhí)行到"彈出對話框"按鈕時候,"彈出對話框"的hitTest:方法同樣會進(jìn)行攔截(雖然點擊的是"對話框",但是因為事件是有傳遞過程的,每傳遞到一個控件時,都會執(zhí)行控件的hitTest方法),self.popBtn此時已經(jīng)被對話框(小孩)btn賦值,所以就會執(zhí)行return self.popBtn;就會執(zhí)行“對話框”按鈕的響應(yīng)事件,即點擊“對話框”按鈕有反應(yīng)(popBtn里面之前已經(jīng)存儲了不惦記btn的圖片和點擊btn時圖片的狀態(tài)以及尺寸等).注意:不會來到btnClick方法,因為你點擊的是"對話框“按鈕(點在對話框"按鈕身上).如果返回的是系統(tǒng)原有的做法return [super hitTest:point withEvent:event];,那肯定是執(zhí)行btnClick方法(就相當(dāng)于沒重寫htiTest方法,即:點擊誰,就執(zhí)行誰的btnClick)。
//如果 一個子控件超出父控件的大小, 默認(rèn)情況下是不能處理的事件.
//點擊"彈出對話框"按鈕的子控件是 "對話框"按鈕. 并且子控件超出了父控件的范圍
//精華:點擊對話框按鈕,事件傳遞過程:APPLication--->UIWindow--->按鈕,執(zhí)行到按鈕時,在按鈕的hitTest方法中先判斷出按鈕能接收事件,但是點不在按鈕身上(因為子控件超出了父控件的大小,并且當(dāng)前點是父控件未包含的區(qū)域,)所以不會執(zhí)行第三步(不會從后往前遍歷按鈕的子控件,),就會執(zhí)行第四步:沒有符合條件的子控件,那么就自己最適合處理,所以"彈出對話框"按鈕自己處理
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//通過sender.popBtn = btn;拿到對話框按鈕btn,在這個類中用self.popBtn表示
//point是點擊"對話框"按鈕中的某個點(當(dāng)然第一次點擊"彈出對話框"時,point指的就是那時的點,第二次點擊時,才是“對話框”按鈕的點)
NSLog(@"%@--",self.popBtn);
//如果點在自己的子控件上面.讓子控件處理事件.
if (self.popBtn) {
//把當(dāng)前點轉(zhuǎn)換成"對話框"坐標(biāo)系上面的點
CGPoint popBtnP = [self convertPoint:point toView:self.popBtn];
if ( [self.popBtn pointInside:popBtnP withEvent:event]) {//判斷點在不在對話框"按鈕身上
return self.popBtn;
}else {
return [super hitTest:point withEvent:event];//點不在對話框"按鈕身上,保持系統(tǒng)原有的做法
}
}
return [super hitTest:point withEvent:event];
}
//
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//獲取UITouch
UITouch *touch = [touches anyObject];
//獲取當(dāng)前手指的點
CGPoint curP = [touch locationInView:self];
//獲取上一個手指所在的點
CGPoint preP = [touch previousLocationInView:self];
//計算偏移量
CGFloat offsetX = curP.x - preP.x;
CGFloat offsetY =curP.y - preP.y;
//平移
self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
}
@end
ViewController.h文件
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
ViewController.m文件
#import "ViewController.h"
#import "ChatBtn.h"
@interface ViewController ()
@end
@implementation ViewController
//重:因為"彈出對話框"按鈕已經(jīng)和ChatBtn綁定,所以點擊"彈出對話框",傳遞的sender的類型是CharBtn類型
- (IBAction)btnClick:(ChatBtn *)sender {
//添加一個子控件
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@"對話框"] forState:UIControlStateNormal];
//UIControlStateHighlighted可以添加圖片,UIControlStateSelected不可以添加圖片,只能被選中
[btn setImage:[UIImage imageNamed:@"小孩"] forState:UIControlStateHighlighted];
btn.frame = CGRectMake(50, -80, 100, 80);
//btn是UIButton類型的指針,popBtn也是UIButton類型的指針,所以可以賦值
sender.popBtn =btn;
//sender.popBtn =(ChatBtn *)btn;
[sender addSubview:btn];
}
@end
Main.storyboard文件
觸摸事件處理的詳細(xì)過程
用戶點擊屏幕后產(chǎn)生的一個觸摸事件,經(jīng)過一系列的傳遞過程后,會找到最合適的視圖控件來處理這個事件
找到最合適的視圖控件后,就會調(diào)用控件的touches方法來作具體的事件處理(touchesBegan…,touchesMoved…,touchedEnded…)
touchesBegan方法的默認(rèn)做法是將事件順著響應(yīng)者鏈條向上傳遞(父類),將事件交給上一個響應(yīng)者進(jìn)行處理
事件響應(yīng)代碼
- 上一個響應(yīng)者
- 1.點擊當(dāng)前的View,如果當(dāng)前的View是控制器的View.那么它的上一個響應(yīng)者就是所在的控制器
- 2.如果不是控制器View,那么它的上一個響應(yīng)者就是它的父控件
//重寫系統(tǒng)的方法--->保持系統(tǒng)默認(rèn)做法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];//保持系統(tǒng)默認(rèn)做法
}
//重寫系統(tǒng)的方法--->處理事件,沒有保持系統(tǒng)默認(rèn)做法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@---touch",[self class]); //處理事件(打印當(dāng)前類),沒有保持系統(tǒng)默認(rèn)做法
}
//重寫系統(tǒng)的方法--->處理事件,并保持系統(tǒng)默認(rèn)做法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@---touch",[self class]);//處理事件(打印當(dāng)前類)
[super touchesBegan:touches withEvent:event];//保持系統(tǒng)默認(rèn)做法,即順著響應(yīng)者鏈條傳遞給上一個響應(yīng)者(父類)。
}
- E:左側(cè)的圖:事件傳遞到了綠色view
- 如果綠色view不處理(事件),就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(1)向上傳遞給黃色View
- 如果黃色view也不處理(事件),就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(2)向上傳遞給青色View(控制器的view)。
- 如果青色view(控制器的view)也不處理(事件),就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(3)向上傳遞給View Controlleer控制器
- 如果View Controlleer控制器也不處理事件,就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(4)向上傳遞給window(窗口)
- 如果window(窗口)也不處理事件,就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(5)向上傳遞給Application
- 此時Application發(fā)現(xiàn)沒有人處理事件,那么這個事件就會被拋棄掉
- F:右側(cè)的圖:事件傳遞到了綠色view
- 如果綠色view不處理(事件),就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(1)向上傳遞給黃色View(子控制器的view)
- 如果黃色view(子控制器的view)也不處理,就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(2)向上傳遞給iew Controlleer控制器
- 如果View Controlleer控制器也不處理事件,就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(3)向上傳遞給青色view(父控制器的view)
- 如果青色view(父控制器的view)也不處理(事件),就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條向上傳遞給View Controlleer控制器
- 如果View Controlleer控制器也不處理事件,就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(4)向上傳遞window(窗口)
- 如果window(窗口)也不處理事件,就會保持系統(tǒng)默認(rèn)的做法,沿著響應(yīng)者鏈條(5)向上傳遞給Application
- 此時Application發(fā)現(xiàn)沒有人處理事件,那么這個事件就會被拋棄掉
事件的傳遞和響應(yīng)過程(看ABCDEF): 傳遞:在hitTest方法中找到適合的view(A,B,C,D) 響應(yīng):在toucherBegan方法中處理事件(E或F)
UIGestureRecognizer(手勢識別器)
- UIGestureRecognizer是一個抽象類,定義了所有手勢的基本行為,使用它的子類才能處理具體的手勢
- UITapGestureRecognizer(敲擊)
- UIPinchGestureRecognizer(捏合,用于縮放)
- UIPanGestureRecognizer(拖拽)
- UISwipeGestureRecognizer(輕掃)
- UIRotationGestureRecognizer(旋轉(zhuǎn))
- UILongPressGestureRecognizer(長按)
- 如果將手勢識別的代理方法(下面的方法):支持多個手勢設(shè)置為NO,那么系統(tǒng)默認(rèn)只能識別一個手勢.
- 例如:果你添加了2個手勢(旋轉(zhuǎn),縮放)到代碼中,當(dāng)你運行時,旋轉(zhuǎn)圖片的時候,不能縮放, 縮放圖片的時候不能旋轉(zhuǎn)
//是否允許支持多個手勢
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return NO;
}
//旋轉(zhuǎn),縮放,位移中:
//第一個參數(shù)是上次(之前)的位置
//第二個參數(shù)(第三個參數(shù))是距離上次(之前)位置的偏移量
//等號左邊的self.imageV.transform是(這次)現(xiàn)在的位置(現(xiàn)在的位置是根據(jù)右邊的參數(shù)計算出來的位置)
//旋轉(zhuǎn)
self.imageV.transform = CGAffineTransformRotate(self.imageV.transform, rotation.rotation);
//縮放
self.imageV.transform = CGAffineTransformScale(self.imageV.transform, pinch.scale, pinch.scale);
//位移
self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y);
手勢識別
#import "ViewController.h"
@interface ViewController ()<UIGestureRecognizerDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageV;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageV.userInteractionEnabled = YES;
[self panGes];
}
//是否允許支持多個手勢
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return NO;
}
//拖動手勢
- (void)panGes {
//1.創(chuàng)建手勢
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
//2.添加手勢
[self.imageV addGestureRecognizer:pan];
}
//當(dāng)拖動時調(diào)用
- (void)pan:(UIPanGestureRecognizer *)pan {
//NSLog(@"%s",__func__);
//獲取偏移量(相對于最原始的值),用transP表示。例如偏移量為100(相對于0來說)(其實是有x和y值)
//pan.view是當(dāng)前手勢的所在view。所在的view是UIImageView。所以pan.view等價于self.imageV;
CGPoint transP = [pan translationInView:pan.view];
NSLog(@"transP=%@",NSStringFromCGPoint(transP));//1
//CGAffineTransformTranslate的第一個參數(shù)是記錄上次的位置,第二個參數(shù)和第三個參數(shù)是偏移量x和偏移量y,只有知道上次的位置和偏移量,才能知道下次的位置
//如果屏蔽2的代碼,當(dāng)你拖動圖片的時候,其實系統(tǒng)底層已經(jīng)記錄了你拖動到了什么位置,只是不顯示。通過1的打印就可以知道,只是我們通過2將拖動到的位置顯示了出來,僅此而已
self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y);//2
//復(fù)位(相對于上一次的操作) ,把偏移量為1進(jìn)行清零,那么下次獲取偏移量時,就相對于0來說,如果不清零,下次真實的偏移量若為200時,就會走100+200=300的距離,但是實際的偏移量為200
[pan setTranslation:CGPointZero inView:pan.view];
}
@end