美文网首页
iOS 基础-02

iOS 基础-02

作者: MR_詹 | 来源:发表于2020-04-26 09:56 被阅读0次

    transform

    特点:
    // 使用Make方法,它是相对 最原始的位置做的形变
    // 不用make方法,它是相对上一次做的形变
    
    // 还原到初始状态
    CGAffineTransformIdentity
    
    比如:平移(其他的旋转rotation、缩放scale都是相同的效果)
    // 只平移一次,即使设置多次 
    self.orgenView.transform = CGAffineTransformMakeTranslation(0, -50);
    
    // 多次设置,会一直往上平移,在上一次的平移的基础上平移
    self.orgenView.transform = CGAffineTransformTranslate(self.orgenView.transform, 0, -50);
    
    
    1) 创建“基于控件初始位置”的形变
      CGAffineTransformMakeTranslation(平移)    
      CGAffineTransformMakeScale(缩放)   
      CGAffineTransformMakeRotation(旋转)   
    
    (2) 创建“基于transform参数”的形变
      CGAffineTransformTranslate   
      CGAffineTransformScale    
      CGAffineTransformRotate
    

    事件

    iOS中的事件可以分为3大类型

    image.png

    iOS中并不是所有的对象都能处理事件,只有基础于UIResponder的类才行

    触摸事件

    /* 一根或者多跟手指开始触摸view,系统会自动触发view的下面方法 **/
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
    
    /* 一根或者多跟手指在view上移动,系统会自动触发view的下面方法(随着手指的移动,会持续调用该方法) **/
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
    
    /* 一根或者多跟手指离开view,系统会自动触发view的下面方法 **/
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
    
    /* 触摸结束前,某个系统事件(例如电弧呼入)会打断触摸过程,系统会自动低啊用view的下面方法 **/
    - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
    
    
    UITouch 
    * 当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象
    * 一根手指对应一个UITouch对象
    * UITouch的作用:保存着跟手指相关的信息,比如触摸的位置、时间、阶段
    * 当手指移动时,系统会更新同一个UITouch对象,使之能一直保存该手指的触摸位置
    * 当手指离开屏幕时,系统会销毁相应的UITouch对象
    
    UITouch属性
    // 记录了触摸时间产生或者变化时的时间,单位是秒
    @property(nonatomic,readonly) NSTimeInterval      timestamp;
    // 当前触摸事件所处的状态
    @property(nonatomic,readonly) UITouchPhase        phase;
    // 短时间内点击屏幕的次数,可以根据tapCount判断单击、双击或者更多的点击
    @property(nonatomic,readonly) NSUInteger          tapCount;   // touch down within a certain point within a certain amount of time
    @property(nonatomic,readonly) UITouchType         type API_AVAILABLE(ios(9.0));
    
    @property(nonatomic,readonly) CGFloat majorRadius API_AVAILABLE(ios(8.0));
    @property(nonatomic,readonly) CGFloat majorRadiusTolerance API_AVAILABLE(ios(8.0));
    
    // 触摸产生时所处的窗口
    @property(nullable,nonatomic,readonly,strong) UIWindow                        *window;
    // 触摸产生时所处的视图view
    @property(nullable,nonatomic,readonly,strong) UIView                          *view;
    @property(nullable,nonatomic,readonly,copy)   NSArray <UIGestureRecognizer *> *gestureRecognizers
    
    UITouch方法
    // 返回值表示触摸在view上的位置
    // 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0,0))
    // 调用时传入的view参数为nil,返回的是触摸点在UIWindow的位置
    - (CGPoint)locationInView:(nullable UIView *)view;
    
    // 记录了前一个触摸点的位置
    - (CGPoint)previousLocationInView:(nullable UIView *)view;
    
    
    实例:手指拖动红框
    // VC
    #import "SeconderViewController.h"
    #import "RedView.h"
    @interface SeconderViewController ()
    @property (nonatomic, strong, readwrite) RedView      *redView;
    @end
    
    @implementation SeconderViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.redView = [[RedView alloc]initWithFrame:CGRectMake(100, 100, 50, 50)];
        self.redView.backgroundColor = [UIColor redColor];
        [self.view addSubview:self.redView];
    }
    @end
    
    //  RedView
    #import "RedView.h"
    @implementation RedView
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 方法原理:通过当前的位置减去上一个位置,计算得出平移量,然后通过transform平移
        UITouch *touch = [touches anyObject];
        // 求偏移量
        CGPoint curPoint = [touch locationInView:self];
        CGPoint prePoint = [touch previousLocationInView:self];
    
        CGFloat offsetX = curPoint.x - prePoint.x;
        CGFloat offsetY = curPoint.y - prePoint.y;
        self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
    }
    @end
    
    
    拖动效果.gif
    UIEvent
    * 每产生一个事件,就会产生一个UIEvent对象
    * UIEvent,称为事件对象,记录事件产生的时刻和类型
    
    常见属性
    // 事件类型
    @property(nonatomic,readonly) UIEventType     type API_AVAILABLE(ios(3.0));
    @property(nonatomic,readonly) UIEventSubtype  subtype API_AVAILABLE(ios(3.0));
    // 事件产生的时间
    @property(nonatomic,readonly) NSTimeInterval  timestamp;
    // 事件产生的所有touch
    @property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches;
    
    // 获取事件在对应的视图上产生的所有touch
    - (nullable NSSet <UITouch *> *)touchesForWindow:(UIWindow *)window;
    - (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view;
    
    

    事件的产生和传递

    * 发送的触摸事件后,系统就会将事件加入到一个`UIApplication`管理的事件`队列`中
    * `UIApplicaiton`会从事件队列中取出`最前面`的事件,并将事件分发下去以便处理,通常先发送事件给应用程序的`主窗口(keyWindow)`
    * 主窗口会在视图层次结构中`找到一个最合适的视图来处理触摸事件`,这也是整个事件处理过程的第一步
    * 找到合适的视图控件后,就会调用视图控件的`touches方法`来做具体的事件处理
    * 触摸事件的传递是`从父控件传递到子控件`
    * 如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件
    (UIView不接收触摸事件的三种情况
      1>不接受用户交互 userInteractionEnable = NO
      2>隐藏 hidden = NO
      3>透明 alpha = 0.0 ~ 0.01
    )
    
    =====》`如何找到最合适的控件来处理事件`
    a>自己是否能接收出发事件
    b>触摸点是否在自己身上
    c>从后往前遍历子控件,重复前面的两个步骤
    d>如果没有符合条件的子控件,那么就自己最合适处理
    
    // 作用:去寻找最合适的View
    // 什么时候调用:当一个事件传递给当前的view,就会调用
    // 返回值:返回的是谁,谁就是最合适的view(就会调用最合适的view的touch方法)
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        return [super hitTest:point withEvent:event];
    }
    
    // 作用:判断当前点在不在它调用View,(谁调用pointInside,这个view就是谁)
    // 什么时候调用:它是在hitTest方法当中调用的
    // 注意:point点必须要跟它方法调用者在同一个坐标系里面(也就是:point是以当前view的左上角为坐标原点)
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
        return [super pointInside:point withEvent:event];
    }
    
    
    `hitTest `内部实现大概如下 
    
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        // 1.0 判断自己能否接收事件
        if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.1) {
            return nil;
        }
        // 2. 判断当前点在不在当前view
        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 *chilView = self.subviews[i];
            // 把当前的点转换成子控件坐标系上的点
            CGPoint childPoint =  [self convertPoint:point toView:chilView];
            UIView *fitView = [chilView hitTest:childPoint withEvent:event];
            if (fitView) {
                return fitView;
            }
        }
        
        // 4. 没有找到让它自己更合适的view,那么它自己就是最适合的view
        return self;
    }
    
    ========== `事件传递的完整过程` =========
    1.先将事件对象由上往下传递(由父控件传递给子控件),找到最合适的控件来处理这个事件
    2.调用最适合控件的touches...方法
    3.如果调用了[super touches...]就会将事件顺着响应者链条往上传递,传递给上一个响应者
    4.接着就会调用上一个响应者的touches...方法
     >>>>>>>>>> 如何判断上一个响应者 
    1.如果当前这个view是控制器的view,那么控制器就是上一个响应者
    2.如果当前这个view不是控制器的view,那么父控件就是上一个响应者 
    
    

    相关文章

      网友评论

          本文标题:iOS 基础-02

          本文链接:https://www.haomeiwen.com/subject/ksppuhtx.html