iOS開發學習之觸控事件和手勢識別

chenyxh2005發表於2014-12-09
iOS的輸入事件
觸控事件
手勢識別
手機搖晃
一、iOS的輸入事件
 
觸控事件(滑動、點選)
運動事件(搖一搖、手機傾斜、行走),不需要人為參與的
遠端控制事件(耳機控制手機聲音)
1⃣️iOS事件物件都是UIEvent類的例項
UIEvent類對事件型別定義了enum常量:
typedef NS_ENUM(NSInteger, UIEventType){
     UIEventTypeTouches,
     UIEventTypeMotion,
     UIEventRemoteControl,
};
觸控事件必須是繼承UIResponser的
二、觸控事件
1⃣️UIView,有4種處理不同的觸控事件
UIView是UIResponder的子類,可以覆蓋下列4個方法處理不同的觸控事件。
1. 一根或者多根手指開始觸控螢幕
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
2.一根或者多根手指在螢幕上移動(隨著手指的移動,會持續呼叫該方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
3.一根或者多根手指離開螢幕
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
4.觸控結束前,某個系統事件(例如電話呼入)會打斷觸控過程
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
複製程式碼
#pragma mark - UITouch事件
#pragma mark 觸控開始
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"觸控開始");
    for (UITouch *touch in touches) {
        NSLog(@"%@", touch);
    }
}
 
#pragma mark 觸控移動
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"觸控移動Touch物件個數:%d",[touches count]);
    // 要移動介面上黃顏色的檢視
    
    // 1. 得到當前手指的位置
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];
    // 2. 得到上一次手指的位置
    CGPoint preLocation = [touch previousLocationInView:self.view];
    // 3. 計算兩個位置之間的偏移
    CGPoint offset = CGPointMake(location.x - preLocation.x, location.y - preLocation.y);
    // 4. 使用計算出來的偏移量,調整檢視的位置
    [_demoView setCenter:CGPointMake(_demoView.center.x + offset.x, _demoView.center.y + offset.y)];
    
    // 完整的UITouch事件除錯方法
    NSLog(@"觸控移動");
    for (UITouch *touch in touches) {
        NSLog(@"%@", touch);
    }
}
 
#pragma mark 觸控結束
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 完整的UITouch事件除錯方法
    NSLog(@"觸控完成");
    for (UITouch *touch in touches) {
        NSLog(@"%@", touch);
    }
}
 
#pragma mark 觸控中斷
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 完整的UITouch事件除錯方法
    NSLog(@"觸控中斷");
    for (UITouch *touch in touches) {
        NSLog(@"%@", touch);
    }
}
複製程式碼
2⃣️觸控事件的處理
如果hit-test檢視無法處理事件,則通過響應者鏈向上傳遞
1.如果hit-test檢視的控制器存在,就傳遞給控制器;如果控制器不存在,則將其傳遞給它的父檢視
2.如果檢視或它的控制器無法處理收到的事件或訊息,則將其傳遞給該檢視的父檢視
3.每一個在檢視繼承樹中的上層檢視如果不能處理收到的事件或訊息,則重複上面的步驟1,2
4.在檢視繼承樹的最上層檢視,如果也不能處理收到的事件或訊息,則其將事件或訊息傳遞給視窗物件進行處理
5. 如果視窗物件也不能進行處理,則其將事件或訊息傳遞給UIApplication物件
6.如果UIApplication也不能處理該事件或訊息,則將其丟棄
當使用者點選螢幕時,會產生一個UITouch物件傳遞給UIApplication,然後由window負責查詢最適合相應觸控事件的檢視物件(hitTest,pointInside)
找到合適的檢視之後,Touch方法由對應的檢視完成,上級檢視不再接管
3⃣️不接受處理事件的三種方法
不接收使用者互動:userInteractionEnabled = NO;
隱藏:hidden = YES;
透明:alpha = 0~0.01
三、手勢識別
1⃣️iOS目前支援的手勢識別(6種)
UITapGestureRecognizer(點按)
UIPinchGestureRecognizer(捏合)
UIPanGestureRecognizer(拖動)
UISwipeGestureRecognizer(輕掃)
UIRotationGestureRecognizer(旋轉)
UILongPressGestureRecognizer(長按)
2⃣️手勢識別的使用方法(4步)
通常在檢視載入的時候定義(UIGestureRecognizer是抽象類,需要例項化使用)
建立手勢識別例項
設定手勢識別屬性,例如手指數量,方向等
將手勢識別附加到指定的檢視之上
編寫手勢觸發響應方法
3⃣️手勢識別的狀態(7個)
   1.  // 沒有觸控事件發生,所有手勢識別的預設狀態
    UIGestureRecognizerStatePossible,
    // 一個手勢已經開始但尚未改變或者完成時
    UIGestureRecognizerStateBegan,
    // 手勢狀態改變
    UIGestureRecognizerStateChanged,
    // 手勢完成
    UIGestureRecognizerStateEnded,
    // 手勢取消,恢復至Possible狀態
    UIGestureRecognizerStateCancelled, 
    // 手勢失敗,恢復至Possible狀態
    UIGestureRecognizerStateFailed,
    // 識別到手勢識別
    UIGestureRecognizerStateRecognized =UIGestureRecognizerStateEnded 
  2.手勢識別的屬性
state——手勢狀態
view——手勢發生檢視
常用方法
locationInView 獲得手勢發生對應檢視所在位置
複製程式碼
- (void)viewDidLoad
{
    [super viewDidLoad];
   
    // 根據例項化方法,我們知道:
    // 1.有一個處理訊息的物件,應該是self
    // 2.我們需要定義一個方法,當手勢識別檢測到的時候,執行
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
    // setNumberOfTapsRequired 點按次數
    [tap setNumberOfTapsRequired:1];
    // setNumberOfTouchesRequired 點按的手指數量
    [tap setNumberOfTouchesRequired:1];
    // 把手勢識別增加到檢視上
    [self.demoView addGestureRecognizer:tap];
    
   
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchAction:)];
    [self.demoView addGestureRecognizer:pinch];
    
   
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotationAction:)];
    [self.demoView addGestureRecognizer:rotation];
    
   
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panAction:)];
    [self.demoView addGestureRecognizer:pan];
    
   
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressAction:)];
    [self.demoView addGestureRecognizer:longPress];
    
   
    // 向左掃
    UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
    [swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
    [self.view addGestureRecognizer:swipeLeft];
    // 向右掃
    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
    [swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
    [self.view addGestureRecognizer:swipeRight];
    // 向上掃
    UISwipeGestureRecognizer *swipeTop = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
    [swipeTop setDirection:UISwipeGestureRecognizerDirectionUp];
    [self.view addGestureRecognizer:swipeTop];
    // 向下掃
    UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
    [swipeDown setDirection:UISwipeGestureRecognizerDirectionDown];
    [self.view addGestureRecognizer:swipeDown];
}
 
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
 
#pragma mark - 輕掃手勢
- (void)swipeAction:(UISwipeGestureRecognizer *)sender
{
    NSLog(@"%d", sender.direction);
    switch (sender.direction) {
        case UISwipeGestureRecognizerDirectionLeft:
            NSLog(@"向左掃");
            break;
        case UISwipeGestureRecognizerDirectionRight:
            NSLog(@"向右掃");
            break;
        case UISwipeGestureRecognizerDirectionUp:
            NSLog(@"向上掃");
            break;
        case UISwipeGestureRecognizerDirectionDown:
            NSLog(@"向下掃");
            break;
        default:
            break;
    }
}
 
#pragma mark - 長按手勢
- (void)longPressAction:(UILongPressGestureRecognizer *)sender
{
    // 我們可以利用demoView的Tag屬性,預設時tag=0
    // 如果tag=0,我們放大一倍,否則,我們縮小一半
    CGFloat scale;
    if (_demoView.tag == 0) {
        scale = 2.0;
        _demoView.tag = 1;
    } else {
        scale = 0.5;
        _demoView.tag = 0;
    }
    
    sender.view.transform = CGAffineTransformScale(sender.view.transform, scale, scale);
}
 
#pragma mark - 拖放手勢
- (void)panAction:(UIPanGestureRecognizer *)sender
{
    // 在拖放手勢中是需要考慮手指的狀態的UIGestureRecognizerState
    // 在拖放手勢中使用的狀態是UIGestureRecognizerStateChanged
    // 通常在使用拖放手勢的時候,當手指離開的時候,應該做一個很小的動作,提醒使用者拖放完成
    if (sender.state == UIGestureRecognizerStateChanged) {
        // locationInView
        [_demoView setCenter:[sender locationInView:self.view]];
    } else if (sender.state == UIGestureRecognizerStateEnded) {
        [_demoView setBackgroundColor:[UIColor yellowColor]];
    }
}
 
#pragma mark - 旋轉手勢
- (void)rotationAction:(UIRotationGestureRecognizer *)sender
{
    sender.view.transform = CGAffineTransformRotate(sender.view.transform, sender.rotation);
    
    // 和捏合操作類似,旋轉角度同樣需要方福偉
    sender.rotation = 0.0f;
}
 
#pragma mark - 捏合手勢
- (void)pinchAction:(UIPinchGestureRecognizer *)sender
{
    // 有關轉換的內容,我們在後續動畫部分再繼續
    sender.view.transform = CGAffineTransformScale(sender.view.transform, sender.scale, sender.scale);
    
    // 縮放功能很簡單,但是不要忘記將比例復位
    sender.scale = 1.0f;
    NSLog(@"捏我了");
}
 
#pragma mark - 點按手勢
- (void)tapAction:(UITapGestureRecognizer *)sender
{
   
    NSLog(@"點我了 %@", sender);
}
 
#pragma mark - 手勢觸控事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"觸控事件!");
    // 1. 先取出UITouch物件
    // 2. 判斷響應點選的UIView是不是我們需要的
    UITouch *touch = [touches anyObject];
    if ([touch view] == _imageView) {
        NSLog(@"點到影象了!");
    }
}
複製程式碼
四、手機搖晃
1. 新建搖晃監聽檢視ShakeListenerView,並且設定canBecomeFirstResponder返回YES
- (BOOL)canBecomeFirstResponder
{
    return YES;
}
2. 在Storyboard中將ViewController的View的Class設定為:ShakeListenerView
3. 在ViewController.m檔案中增加:viewDidAppear和viewDidDisappear在檢視出現和消失時成為/撤銷第一響應者身份
4. 在檢視控制器中增加手勢監聽方法:
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (event.subtype == UIEventSubtypeMotionShake) {
        NSLog(@"shake phone");
    }
}
複製程式碼
#pragma mark - 要讓ViewController支援搖晃,需要寫三個方法
// 1. 成為第一響應者,檢視一出現時,就應該成為第一響應者
- (void)viewDidAppear:(BOOL)animated
{
    [self.view becomeFirstResponder];
    // 不要忘記去實現父類方法
    [super viewDidAppear:animated];
}
 
// 2. 登出第一響應者,檢視要關閉的時候,登出
- (void)viewDidDisappear:(BOOL)animated
{
    [self.view resignFirstResponder];
    // 不要忘記去實現父類方法
    [super viewDidDisappear:animated];
}
 
// 3. 監聽並處理移動事件,判斷是否搖晃了手機
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake) {
        NSLog(@"搖啊搖,搖到外婆橋!!!");
    }
}

相關文章