iOS 基础-02

2020-04-26  本文已影响0人  MR_詹

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,那么父控件就是上一个响应者 

上一篇下一篇

猜你喜欢

热点阅读