CALayer觸摸事件擴展

在控制器中寫下如下代碼

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *layerView;//在Storyboard中拖入的視圖
@property (nonatomic, strong) CALayer *blueLayer;//此處一定要強引用,否則會被銷毀無法顯示
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    self.blueLayer = [CALayer layer];
    self.blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
    self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add it to our view
    [self.layerView.layer addSublayer:self.blueLayer];
    
}

CALayer并不關心任何響應鏈事件,所以不能直接處理觸摸事件或者手勢。但是它有一系列的方法幫你處理事件:-containsPoint:和-hitTest:。
-containsPoint:接受一個在本圖層坐標系下的CGPoint,如果這個點在圖層frame范圍內就返回YES。使用-containsPoint:方法來判斷到底是白色還是藍色的圖層被觸摸了。這需要把觸摸坐標轉換成每個圖層坐標系下的坐標,結果很不方便。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get touch position relative to main view
    CGPoint point = [[touches anyObject] locationInView:self.view];

    NSLog(@"%f,%f",point.x ,point.y);
    //convert point to the white layer's coordinates
    point = [self.layerView.layer convertPoint:point fromLayer:self.view.layer];
    NSLog(@"-----%f,%f",point.x ,point.y);
    //get layer using containsPoint:
    if ([self.layerView.layer containsPoint:point]) {
        //convert point to blueLayer’s coordinates
        point = [self.blueLayer convertPoint:point fromLayer:self.layerView.layer];
        NSLog(@"++++++%f,%f",point.x ,point.y);
        if ([self.blueLayer containsPoint:point]) {
            [[[UIAlertView alloc] initWithTitle:@"Inside Blue Layer"
                                        message:nil
                                       delegate:nil
                              cancelButtonTitle:@"OK"
                              otherButtonTitles:nil] show];
        } else {
            UIAlertController * alertView = [UIAlertController alertControllerWithTitle:@"Inside White Layer" message:nil preferredStyle:UIAlertControllerStyleAlert];
            [alertView addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                NSLog(@"成功了");
            }]];
            [self presentViewController:alertView animated:true completion:nil];
//            [self performSelector:@selector(action) withObject:nil afterDelay:3];
        }
    }
}

-hitTest:方法同樣接受一個CGPoint類型參數,而不是BOOL類型,它返回圖層本身,或者包含這個坐標點的葉子節點圖層。這意味著不再需要像使用-containsPoint:那樣,人工地在每個子圖層變換或者測試點擊的坐標。如果這個點在最外面圖層的范圍之外,則返回nil。具體使用-hitTest:方法被點擊圖層的代碼如下。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get touch position
    CGPoint point = [[touches anyObject] locationInView:self.view];
    //get touched layer
    CALayer *layer = [self.layerView.layer hitTest:point];
    //get layer using hitTest
    if (layer == self.blueLayer) {
        [[[UIAlertView alloc] initWithTitle:@"Inside Blue Layer"
                                    message:nil
                                   delegate:nil
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    } else if (layer == self.layerView.layer) {
        [[[UIAlertView alloc] initWithTitle:@"Inside White Layer"
                                    message:nil
                                   delegate:nil
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
    }
}

注意當調用圖層的-hitTest:方法時,測算的順序嚴格依賴于圖層樹當中的圖層順序(和UIView處理事件類似)。之前提到的zPosition屬性可以明顯改變屏幕上圖層的順序,但不能改變事件傳遞的順序。
這意味著如果改變了圖層的z軸順序,你會發現將不能夠檢測到最前方的視圖點擊事件,這是因為被另一個圖層遮蓋住了,雖然它的zPosition值較小,但是在圖層樹中的順序靠前。

歡迎關注我的微博博客

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容