title: 實(shí)戰(zhàn)1.2-利用手勢識(shí)別器,實(shí)現(xiàn)視圖的手勢控制
知識(shí)預(yù)備
- 什么是仿射變換?
從視覺效果上來理解,仿射變換是通過一系列原子變換復(fù)合而來的變換。包括:平移(Translation)、縮放(Scale)、翻轉(zhuǎn)(Flip)、旋轉(zhuǎn)(Rotation)和錯(cuò)切(Shear)(圖像的錯(cuò)切實(shí)際上是平面景物在投影平面上的非垂直投影)。
- iOS 視圖的 frame 和 bounds 屬性的區(qū)別
frame 描述了該視圖在父視圖坐標(biāo)系統(tǒng)中的位置和大小,其參照點(diǎn)是父視圖的坐標(biāo)系統(tǒng)。
bounds 描述了該視圖在本地坐標(biāo)系統(tǒng)中的位置和大小,其參照點(diǎn)是該視圖自己的坐標(biāo)系統(tǒng)。
如果還有疑惑,可以點(diǎn)擊下方鏈接,這篇博文確切地解釋了區(qū)別所在。
iOS 視圖的 frame 和 bounds 屬性的區(qū)別
3.UIGestureRecognizerState 的參數(shù)值(狀態(tài))
UIGestureRecognizerStatePossible : 手勢識(shí)別器還沒有識(shí)別出手勢,但是可能正在估算觸摸事件(touches event),還處在判斷階段。這個(gè)狀態(tài)是手勢識(shí)別器的默認(rèn)狀態(tài)。
UIGestureRecognizerStateBegan : 手勢識(shí)別器已經(jīng)接收到了一系列觸摸并且識(shí)別出了它屬于哪個(gè)手勢。響應(yīng)方法在下一個(gè)運(yùn)行周期被調(diào)用。
UIGestureRecognizerStateChanged : 手勢識(shí)別器已經(jīng)接收到了一系列觸摸并且識(shí)別出手勢發(fā)生了改變。響應(yīng)方法在下一個(gè)運(yùn)行周期被調(diào)用。
UIGestureRecognizerStateEnded : 手勢識(shí)別器已經(jīng)接收到了一系列觸摸并且識(shí)別出手勢剛剛結(jié)束。響應(yīng)方法在下一個(gè)運(yùn)行周期被調(diào)用,并且把手勢狀態(tài)的值重新置為 UIGestureRecognizerStatePossible。
UIGestureRecognizerStateCancelled : 手勢識(shí)別器已經(jīng)接收到了一系列觸摸并且識(shí)別出手勢突然中斷。響應(yīng)方法在下一個(gè)運(yùn)行周期被調(diào)用,并且把手勢狀態(tài)的值重新置為 UIGestureRecognizerStatePossible。
UIGestureRecognizerStateFailed : 手勢識(shí)別器已經(jīng)接收到了一系列多點(diǎn)觸控,但是與識(shí)別器認(rèn)識(shí)的手勢匹配失敗(識(shí)別不出來)。無響應(yīng)方法。把手勢狀態(tài)的值重新置為 UIGestureRecognizerStatePossible。
UIGestureRecognizerStateRecognized : 手勢識(shí)別器已經(jīng)接收到了一系列多點(diǎn)觸控,并且識(shí)別器識(shí)別出該手勢。響應(yīng)方法在下一個(gè)運(yùn)行周期被調(diào)用,并且把手勢狀態(tài)的值重新置為 UIGestureRecognizerStatePossible。
實(shí)戰(zhàn)
創(chuàng)建工程步驟請參照 實(shí)戰(zhàn)1中的前幾步。
在工程中創(chuàng)建一個(gè) MyImageView 視圖類,繼承自 UIImageView。
-
首先為我們創(chuàng)建的視圖類進(jìn)行初始化方法的編寫,在 MyImageView.m 文件中:
static int count; @implementation MyImageView { CGPoint previousLocation; UIPanGestureRecognizer *panGestureRecognizer; } #pragma mark - initialization - (id) initWithImage:(UIImage *)image { self = [super initWithImage:image]; if (self) { self.userInteractionEnabled = YES; // 在視圖被初始化的過程中,創(chuàng)建一個(gè) 拖動(dòng)手勢識(shí)別器 // 應(yīng)用 target-action 模式,以該類為目標(biāo)對象,如果手勢被觸發(fā),handlePan: 方法會(huì)被調(diào)用 panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; // 該類的實(shí)例對象能夠被 拖動(dòng)手勢識(shí)別器 所識(shí)別 self.gestureRecognizers = @[panGestureRecognizer]; } return self; } - (id) init { // 圖片文件的名稱為 blue.png return [self initWithImage:[UIImage imageNamed:@"blue"]]; } #pragma mark - touches mothods are called // 當(dāng)該類的實(shí)例對象被觸碰的時(shí)候,該方法會(huì)被觸發(fā) - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.superview bringSubviewToFront:self]; // 視圖中心點(diǎn) previousLocation = self.center; } #pragma mark - selector handlePan: - (void) handlePan:(UIPanGestureRecognizer *) gestureRecognizer { // print 該方法的運(yùn)行次數(shù) NSLog(@"%i", ++count); // 根據(jù)視圖的仿射變換來獲取偏移量 CGPoint translation = [gestureRecognizer translationInView:self.superview]; // 重置視圖中心點(diǎn) self.center = CGPointMake(previousLocation.x + translation.x, previousLocation.y + translation.y); }
-
在 ViewController.h 文件中聲明屬性:
@class MyImageView; @interface ViewController : UIViewController @property (nonatomic, strong) MyImageView *myImageView;
-
在 ViewController.m 文件中導(dǎo)入頭文件(#import "MyImageView.h")并寫入代碼:
- (void)viewDidLoad { [super viewDidLoad]; // 創(chuàng)建類實(shí)例并且添加視圖 _myImageView = [[MyImageView alloc] initWithImage:[UIImage imageNamed:@"blue"]]; [self.view addSubview:_myImageView]; }
-
運(yùn)行結(jié)果: 視圖能夠響應(yīng)拖動(dòng)手勢,在屏幕上被拖動(dòng)。控制臺(tái)顯示結(jié)果標(biāo)明, handlePan: 方法在拖動(dòng)手勢進(jìn)行過程中,一直都在被調(diào)用,直至手勢停止。console
總結(jié):
** 為了讓自定義視圖能夠響應(yīng)手勢,我們在創(chuàng)建并且初始化視圖類實(shí)例的時(shí)候,就應(yīng)該創(chuàng)建響應(yīng)的手勢識(shí)別器及其會(huì)響應(yīng)的方法,當(dāng)手勢在進(jìn)行過程中時(shí),響應(yīng)方法被反復(fù)調(diào)用,直至手勢停止。為了讓視圖能夠跟隨手勢移動(dòng),我們只需根據(jù)視圖先前的中心位置和被拖動(dòng)以后的偏移位置來重新設(shè)定視圖的中心位置即可。
**