第二篇:CALayer能力之hitTest響應事件

weixin_34321977發表於2018-01-22

目錄

一、CALayer hitTest響應事件

二、UIView hitTest+pointInside判斷點選的點是否在某個區域內,由指定的檢視響應事件


  • 首先我們明確CALayer的hitTest和UIView的hitTest它倆不一樣,不是為了解決同樣的問題,其實內在有關聯,但是不想闡述了。就實際應用大概瞭解下就行,CALayer的hitTest是為了能讓layer響應事件,而UIView的hitTest+pointInside是為了判斷點選的點是否在某個區域內,從而讓指定的檢視響應事件。本篇只是表明CALayer也是有辦法響應事件的,只不過如果自己維護事件會很麻煩。

一、CALayer hitTest響應事件

前面我們說到要優先使用UIView,因為UIView自己會處理使用者互動,而CALayer則不關心使用者互動,如果使用CALayer我們就得自己維護複雜的使用者互動事件。
例如,現在一個紅色button上放了一個綠色button(綠色button完全在紅色button內部),如果使用UIView的話,因為響應者鏈會優先讓子檢視響應事件,所以點了綠button部分綠button就響應事件,點了紅button部分紅button就響應事件,根本不存在疑惑。但是現在如果非要用layer,一個紅色layer上放了一個綠色layer(綠色layer完全在紅色layer內部),我們就只能通過layer的hitTest方法來判斷點選的點是在那個layer的範圍內,從而響應不同的事件。

//
//  ViewController.m
//  CoreAnimation
//
//  Created by 意一yiyi on 2017/11/13.
//  Copyright © 2017年 意一yiyi. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) UIView *redView;
@property (strong, nonatomic) UIView *greenView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.redView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
    self.redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.redView];
    
    self.greenView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    self.greenView.backgroundColor = [UIColor greenColor];
    [self.redView addSubview:self.greenView];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    // 獲取[UIApplication sharedApplication].delegate.window座標系下點選的點
    CGPoint point = [[touches anyObject] locationInView:[UIApplication sharedApplication].delegate.window];
    
    // 在[UIApplication sharedApplication].delegate.window座標系下,點選了某個點point,返回point所在的layer
    CALayer *layer = [[UIApplication sharedApplication].delegate.window.layer hitTest:point];
    if (layer == self.redView.layer) {

        NSLog(@"點選了紅色區域");
    }else if (layer == self.greenView.layer) {

        NSLog(@"點選了綠色區域");
    }
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

二、UIView hitTest+pointInside判斷點選的點是否在某個區域內,由指定的檢視響應事件

1、問題:

開發中我們會發現,如果一個子檢視超出了父檢視的邊界,那麼子檢視超出父檢視邊界的部分將是不能響應事件的,例如直播的那個中間突出的按鈕,那怎麼辦呢?先看例子:

//
//  CustomView.m
//  CoreAnimation
//
//  Created by 意一yiyi on 2017/12/18.
//  Copyright © 2017年 意一yiyi. All rights reserved.
//

#import "CustomView.h"

@interface CustomView ()

@property (strong, nonatomic) UIButton *redButton;
@property (strong, nonatomic) UIButton *greenButton;

@end

@implementation CustomView

- (instancetype)initWithFrame:(CGRect)frame {
    
    if (self = [super initWithFrame:frame]) {
        
        [self layoutUI];
    }
    
    return self;
}

- (void)layoutUI {
    
    self.frame = CGRectMake(150, 150, 200, 200);
    
    self.redButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    self.redButton.backgroundColor = [UIColor redColor];
    [self.redButton addTarget:self action:@selector(redButtonAction) forControlEvents:(UIControlEventTouchUpInside)];
    [self addSubview:self.redButton];
    
    self.greenButton = [[UIButton alloc] initWithFrame:CGRectMake(-50, -50, 100, 100)];
    self.greenButton.backgroundColor = [UIColor greenColor];
    [self.greenButton addTarget:self action:@selector(greenButtonAction) forControlEvents:(UIControlEventTouchUpInside)];
    [self.redButton addSubview:self.greenButton];
}

- (void)redButtonAction {
    
    NSLog(@"點選了紅色區域");
}

- (void)greenButtonAction {
    
    NSLog(@"點選了綠色區域");
}

@end
1116725-268fda35aae8d0e5.png
1.png

點選超出紅色區域的綠色部分,我們會發現控制檯確實是不列印東西的。

2、解決:

那這時我們就需要重寫一下UIView的- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法來實現這一效果,其中最核心的部分就是通過UIView或者CALayer來判斷點選點是否在指定的區域內。這個方法的作用就是告訴系統當我們點選了螢幕的時候,由誰來響應事件。

我們在CustomView.m裡面新增如下程式碼就可以了:

// UIView判斷點選的點是否在指定區域內
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        
    // 需要先把點選的點轉換到greenButton座標系下
    CGPoint newPoint = [self convertPoint:point toView:self.greenButton];
    
    // 判斷點選點是否在greenButton上
    if ([self.greenButton pointInside:newPoint withEvent:event]) {// 在的話,由自定義greenButton來響應事件
        
        return self.greenButton;
    }else {// 否則由系統自己處理
        
        return [super hitTest:point withEvent:event];
    }
}

相關文章