主题五《事件》

2020-07-18  本文已影响0人  东方奇迹

1、CGAffineTransform

    [UIView animateWithDuration:0.25 animations:^{
        
        /**
         CGAffineTransform带Make与不带Make的区别?
         1、带Make相对于最原始的位置做形变操做,一般用于做一次形变。
         2、不带Make是相对于指定的形变开始进行,相对于上一次操做,一般用于做多次形变。
         */
        
        //平移

//        self.imgV.transform = CGAffineTransformMakeTranslation(0, 10);
//        self.imgV.transform = CGAffineTransformTranslate(self.imgV.transform, 0, 10);
        
        //旋转

//        self.imgV.transform = CGAffineTransformMakeRotation(M_PI_2);
//        self.imgV.transform = CGAffineTransformRotate(self.imgV.transform, M_PI_2);
        
        //缩放

//        self.imgV.transform = CGAffineTransformMakeScale(0.1, 0.1);
//        self.imgV.transform = CGAffineTransformScale(self.imgV.transform, 0.8, 0.8);
        
    }];

2、触摸事件

截屏2020-07-17 上午11.14.18.png 截屏2020-07-17 上午11.19.30.png 截屏2020-07-17 上午11.21.25.png 截屏2020-07-17 上午11.40.32.png 截屏2020-07-17 上午11.42.33.png 截屏2020-07-17 上午11.44.46.png 截屏2020-07-17 上午11.45.26.png

/** 让当前view跟着手指移动 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //获取UITouch对象
    UITouch *touch = [touches anyObject];
    //获取上一个点
    CGPoint preP = [touch previousLocationInView:self];
    //获取当前点
    CGPoint curP = [touch locationInView:self];
    //平移
    CGFloat tx = curP.x - preP.x;
    CGFloat ty = curP.y - preP.y;
    self.transform = CGAffineTransformTranslate(self.transform, tx, ty);
}

截屏2020-07-17 下午1.52.32.png

事件产生和传递
当触摸一个视图时,首先系统会捕捉此事件,并为此事件创建一个UIEvent对象,将此对象加入当前应用程序的事件队列中,然后由UIApplication对象从队列中一个一个取出来进行分发,首选分发给UIWindow对象,然后由UIWindow对象分发给触摸的视图对象,也就是第一响应者对象。

截屏2020-07-17 下午1.55.31.png 截屏2020-07-17 下午1.55.50.png
//作用:寻找最合适的view。
//什么时候调用:当事件传递给当前view时,会调用当前view的hitTest方法。
//返回值:返回谁,谁就是最合适的view,谁就响应事件,就会调用谁的touches方法。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    //1、自己能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
        //不能接收事件
        return nil;
    }
    //2、点是否在自己身上
    if (![self pointInside:point withEvent:event]) {
        //不在自己身上
        return nil;
    }
    //3、从后往前遍历自己的子控件,把事件传递给子控件,调用子控件的hitTest方法
    int count = (int)self.subviews.count;
    for (int i = count - 1; i >= 0; i--) {
        
        //获取子控件
        UIView *childV = self.subviews[I];
        
        //把当前点的坐标系转换成子控件的坐标系
        CGPoint childP = [self convertPoint:point toView:childV];
        UIView *fitView =  [childV hitTest:childP withEvent:event];
        if (fitView) {
            return fitView;
        }
        
    }
        
    //4、如果子控件没有找到最适合的view,那么自己就是最适合的view
    return self;
}

//作用:点在不在自己身上
//什么时候调用:在hitTest方法内部调用
//返回值:YES在当前view身上,No不在
//point: 当前触摸点
//注意:必须要跟方法调用者在同一个坐标系
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return NO;
}

黄色view盖住了红色btn,如何才能点击到红色btn
重写黄色view的hitTest方法,判断点是否在红色btn身上,如果在直接返回红色btn

截屏2020-07-18 下午4.14.00.png 截屏2020-07-17 下午7.29.12.png

如果子控件超出了父控件的大小,子控件如何才能收到事件
重写父控件hitTest方法,判断点是否在子控件身上,如果在直接返回子控件

截屏2020-07-18 下午4.55.13.png 截屏2020-07-18 下午5.04.40.png 截屏2020-07-18 下午5.07.03.png 截屏2020-07-18 下午5.16.50.png

响应者链
事件被交由第一响应者对象处理,如果第一响应者不处理,事件被沿着响应者链向上传递,交给下一个响应者。(首先看当前view是否是控制器的view,如果是,那么下一个响应者就是view所在的控制器;如果不是,下一个响应者就是它的父控件)。如果整个过程都没有响应这个事件,该事件就被丢弃。一般情况下,在响应链中只要有对象处理事件,事件就停止传递,但有时候可以在视图的响应方法中根据一些条件判断来决定是否需要继续传递事件。

3、手势识别

#import "ViewController.h"

@interface ViewController ()<UIGestureRecognizerDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *imageV;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //添加手势
    //一个手势默认是不能够同时支持多个手势的
    //可以通过代理进行设置
    [self pinchGes];
    [self rotationGes];
    
    
}
#pragma mark - 捏合手势
- (void)pinchGes {
    //1、创建捏合手势
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
    
    pinch.delegate = self;
    
    //2、添加手势
    [self.imageV addGestureRecognizer:pinch];
}
- (void)pinch:(UIPinchGestureRecognizer *)pinch {
    
    //放大、缩小
    //获取缩放比例(相对于最原始的比例)
    CGFloat scale = pinch.scale;
    self.imageV.transform = CGAffineTransformScale(self.imageV.transform, scale, scale);
    
    //清0操做
    [pinch setScale:1];
    
}
#pragma mark - 旋转手势
- (void)rotationGes {
    
    //1、创建旋转手势
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
    
    rotation.delegate = self;
    
    //2、添加手势
    [self.imageV addGestureRecognizer:rotation];
    
}
- (void)rotation:(UIRotationGestureRecognizer *)rotation {
    
    //获取旋转角度(已经是弧度)
    //相对于最原始的弧度
    CGFloat rot = rotation.rotation;
    self.imageV.transform = CGAffineTransformRotate(self.imageV.transform, rot);
    
    //清0操做
    [rotation setRotation:0];
    
}
#pragma mark - 平移手势
- (void)panGes {
    //1、创建平移手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
    
    //2、添加手势
    [self.imageV addGestureRecognizer:pan];
}
//当拖动时持续调用
- (void)pan:(UIPanGestureRecognizer *)pan {
    

    //获取偏移量
    //获取的偏移量是相对于最原始的点
    CGPoint transP = [pan translationInView:self.imageV];
    
    self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y);
    
    //清0操做(不让偏移量进行累加,获取的是相对于上一次的值,每一次走的值)
    [pan setTranslation:CGPointMake(0, 0) inView:self.imageV];
    
}
#pragma mark - 轻扫手势
- (void)swipeGes {
    //1、创建点按手势
    UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];

    //默认是向右轻扫
    //设置轻扫的方向
    //一个轻扫手势只能对应一个方向
    swipe.direction = UISwipeGestureRecognizerDirectionLeft;
    
    //2、添加手势
    [self.imageV addGestureRecognizer:swipe];
    
    
    //1、创建点按手势
    UISwipeGestureRecognizer *swipe1 = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];

    //默认是向右轻扫
    //设置轻扫的方向
    //一个轻扫手势只能对应一个方向
    swipe1.direction = UISwipeGestureRecognizerDirectionRight;
    
    //2、添加手势
    [self.imageV addGestureRecognizer:swipe1];
    
}
- (void)swipe:(UISwipeGestureRecognizer *)swipe {
    
    if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
        
        NSLog(@"UISwipeGestureRecognizerDirectionLeft");

    }else if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
        
        NSLog(@"UISwipeGestureRecognizerDirectionRight");

    }
}

#pragma mark - 长按手势
- (void)longPressGes {
    //1、创建点按手势
    UILongPressGestureRecognizer *longP = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longg:)];
        
    //2、添加手势
    [self.imageV addGestureRecognizer:longP];
}
//长按手势分状态,长按移动时,会持续调用
- (void)longg:(UILongPressGestureRecognizer *)longP {
    
    if (longP.state == UIGestureRecognizerStateBegan) {
        NSLog(@"UIGestureRecognizerStateBegan");

    }else if (longP.state == UIGestureRecognizerStateChanged) {
        NSLog(@"UIGestureRecognizerStateChanged");

    }else if (longP.state == UIGestureRecognizerStateEnded) {
        NSLog(@"UIGestureRecognizerStateEnded");

    }
}
#pragma mark - 点按手势
- (void)tapGes {
    //1、创建点按手势
    UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];
    
    tapGes.delegate = self;
    
    //2、添加手势
    [self.imageV addGestureRecognizer:tapGes];
}
//实现手势方法
- (void)tap {
    NSLog(@"%s",__func__);
        
}
#pragma mark - UIGestureRecognizerDelegate
//是否允许接收手指
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    
    //获取当前的点
    CGPoint curP = [touch locationInView:self.imageV];
    
    //判断点是在图片的左边还是右边
    if (curP.x > self.imageV.bounds.size.width/2.0) {
        //如果是右边,返回YES
        return YES;
    }else{
        //如果是左边,返回NO
        return NO;
    }
    
}
//是否允许同时支持多个手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}


@end

1、事件产生和传递?

当触摸一个视图时,首先系统会捕捉此事件,并为此事件创建一个UIEvent对象,将此对象加入当前应用程序的事件队列中,然后由UIApplication对象从队列中一个一个取出来进行分发,首先分发给主UIWindow对象,然后由主UIWindow对象分发给触摸的视图对象,也就是第一响应者对象(最合适的视图来处理触摸事件);如果第一响应者不处理,事件被沿着响应者链向上传递,交给下一个响应者。(首先看当前view是否是控制器的view,如果是,那么下一个响应者就是view所在的控制器;如果不是,下一个响应者就是它的父控件)。如果整个过程都没有响应这个事件,该事件就被丢弃。一般情况下,在响应链中只要有对象处理事件,事件就停止传递,但有时候可以在视图的响应方法中根据一些条件判断来决定是否需要继续传递事件。

2、如何找到最合适的控件来处理事件?

当事件传递给当前view时,会调用当前view的hitTest方法来寻找最合适的view,返回谁,谁就是最合适的view,谁就响应事件,就会调用谁的touches方法。

(1)自己是否能接收触摸事件
(2)触摸点是否在自己身上
(3)从后往前遍历子控件,重复前面的两个步骤
(4)如果没有符合条件的子控件,那么自己就是最合适的控件

3、UIView不能接收事件的三种情况?

(1)不接收用户交互
userInteractionEnabled = NO;
(2)隐藏
hidden = YES;
(3)透明
alpha = 0.0 ~ 0.01;

上一篇 下一篇

猜你喜欢

热点阅读