IOS 触摸事件

来源:互联网 发布:linux ubuntu 服务器版 编辑:程序博客网 时间:2024/05/19 17:03


1.事件的产生传递

产生

当用户触摸屏幕的时候,UIApplication监听到触摸事件,会生成UIEvent传递给UIWindow,然后向下传递事件

传递

事件的传递是由外层传递给内层view的,产生事件后会寻找事件的消费者,来消费事件,如何寻找事件消费者呢
主要涉及的API有:
1.
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
2.
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

方法的作用如下:

/* * point:触摸的点,该点的坐标系是相对当前view的 * event:事件 * 返回的view 就是事件的消费者,不返回,由上层调用者处理 * 通俗点说法就是:告诉我摸了谁,返回的view就是被触摸的view,返回空,就是说我和我的子view没有被触摸 */-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{        //以下粗略模拟下了默认的处理方法的大概实现 相当于 return [super hitTest:point withEvent:event];       //判断自己能否接收事件    if(!self.userInteractionEnabled||self.hidden||self.alpha<=0.01){        return nil;    }        //判断点是否落在了当前view的返回内    if(![self pointInside:point withEvent:event]){        return nil;    }        //判断内部子控件是否有比自己更合适的View        for(UIView *childView in self.subviews){        //将点的坐标系转换为子View的坐标系        CGPoint childPoint=[self convertPoint:point toView:childView];        UIView *hitView=[childView hitTest:childPoint withEvent:event];                if(hitView){//如果子View找到了合适的View            return hitView;        }    }    return self;//没有找到比自己合适的view,返回自己}/* * 判断点是否落在在自己的范围内 * 如果重写了 (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 需要自己调用,hitTest默认的实现中,调用这个方法,判断点是否落在自己的范围内 */ -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{    return YES;}



粗略的概括下,如图:

2.事件的响应

所有UIResponder的子类(UIView,UIViewController)都可以响应触摸方法,确定了最佳的事件响应者之后,会调用事件的响应的方法,消费事件。根据事件的不同阶段会调用响应者的不同方法,相关方法一共4个

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    //开始触摸}-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    //触摸并移动}-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    //所有的触摸手指都离开屏幕}-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    //电话等打断触摸时间,一般直接调用触摸结束的方法就可以了    [self touchesEnded:touches withEvent:event];}

3.触摸UITouch

每次触摸都包含下列信息
  1. 触摸的发生在何处(view,以及其坐标),包括当前的触摸位置和上次的触摸位置
  2. 触摸处于的阶段
  3. 点击的次数
  4. 触摸的发生时间
触摸有生命周期,每个触摸都处于下列的触摸的5个阶段之一
 @property(nonatomic,readonly) UITouchPhase        phase;//通过这个属性获取触摸所处阶段    //开始触摸    UITouchPhaseBegan,             // 触摸开始    UITouchPhaseMoved,             // 手指移动    UITouchPhaseStationary,        // 手指触摸着屏幕但不移动    UITouchPhaseEnded,             // 手指离开屏幕    UITouchPhaseCancelled         // 触摸终端

每个触摸信息都保存在UITouch中,而UITouch保存在UIEvent中,多点触摸(Multiple Touch)需要手动开启
以下为常用方法
 UITouch *touch=[touches anyObject];    //获取所处窗口    UIWindow *window=touch.window;    //所处view    UIView *view=touch.view;    //短时间点击屏幕的次数    NSUInteger tapCount=touch.tapCount;    //触摸时间(变化时间)    NSTimeInterval time=touch.timestamp; //秒        //当前所处view,对应其坐标系的位置点    CGPoint currentPoint=[touch locationInView:touch.view];    //前一个位置的点    CGPoint previousPoint=[touch previousLocationInView:touch.view];

4.涂鸦板

根据手指的移动路径绘制图像

#import "TouchDrawView.h"@implementation TouchDrawView{    UIBezierPath *path;//存储绘制路径}-(void)awakeFromNib{    [super awakeFromNib];    self.multipleTouchEnabled=NO;//禁止多点触摸    path=[UIBezierPath bezierPath];    path.lineWidth=5.0f;    path.lineCapStyle=kCGLineCapRound;    path.lineJoinStyle=kCGLineJoinRound;}-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    [path moveToPoint:[[touches anyObject] locationInView:self]];//起点}-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    [path addLineToPoint:[[touches anyObject] locationInView:self]];    [self setNeedsDisplay];}-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    [path addLineToPoint:[[touches anyObject] locationInView:self]];    [self setNeedsDisplay];}-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    [self touchesEnded:touches withEvent:event];}-(void)drawRect:(CGRect)rect{    [[UIColor redColor] setStroke];    [path stroke];}@end

实现的效果图:

平滑算法---样条插值法

样条插值法:在每两个关键点中插入点,使得保留原来的路径的情况下使得画的线更加平滑,前提是必修有原4个参考点,比如现在1,2,3,4点,就可以在2,3之间插入几个点,使得2到3之间更加平滑,但是由于没有处理角度问题,会出现拐角处出现形变。完善思路是根据1,2和3,4形成的两条线之间的夹角判断是否插入值,如果角度偏小不插入。我没有判断夹角直接添加点,使用的话,需要自己完善。
1.创建贝塞尔曲线获取点的分类
#import "UIBezierPath+Points.h"#define VALUE(_INDEX_) [NSValue valueWithCGPoint:points[_INDEX_]]@implementation UIBezierPath (Points)void getPointsFromPath(void * __nullable info,                       const CGPathElement *  element){    NSMutableArray *pointsArray=(__bridge NSMutableArray *)(info);    CGPathElementType type=element->type;    CGPoint *points=element->points;    if(type!=kCGPathElementCloseSubpath){        [pointsArray addObject:VALUE(0)];        if(type!=kCGPathElementAddLineToPoint&&type!=kCGPathElementMoveToPoint){            [pointsArray addObject:VALUE(1)];        }    }    if(type==kCGPathElementAddCurveToPoint){        [pointsArray addObject:VALUE(2)];    }}-(NSArray *)points{    NSMutableArray *points=[NSMutableArray array];    CGPathApply(self.CGPath, (__bridge void * _Nullable)(points), getPointsFromPath);    return points;}
添加获取贝塞尔曲线平滑的分类
#import "UIBezierPath+Smooth.h"#import "UIBezierPath+Points.h"#define POINT(_INDEX_) [[points objectAtIndex:_INDEX_] CGPointValue]@implementation UIBezierPath (Smooth)-(UIBezierPath *)smoothPath:(int)factor{        NSMutableArray *points=[self.points mutableCopy];        //少于4个点 不做插值    if(points.count<4) return [self copy];    [points insertObject:points[0] atIndex:0];    [points addObject:[points lastObject]];        UIBezierPath *smoothPath=[UIBezierPath bezierPath];        smoothPath.lineWidth=self.lineWidth;        [smoothPath moveToPoint:POINT(0)];    [smoothPath addLineToPoint:POINT(1)];    [smoothPath addLineToPoint:POINT(2)];        for(int i=4;i<points.count;i++){        //获取4个控制点        CGPoint p0=POINT(i-3);        CGPoint p1=POINT(i-2);        CGPoint p2=POINT(i-1);        CGPoint p3=POINT(i);                for(int j=1;j<factor;j++){            float t=(float)j*(1.0f/(float)factor);            float tt=t*t;            float ttt=tt*t;                        CGPoint pi;            pi.x=0.5*(2*p1.x+(p2.x-p0.x)*t+(2*p0.x-5*p1.x+4*p2.x-p3.x)*tt+(3*p1.x-p0.x-3*p2.x+p3.x)*ttt);                        pi.y=0.5*(2*p1.y+(p2.y-p0.y)*t+(2*p0.y-5*p1.y+4*p2.y-p3.y)*tt+(3*p1.y-p0.y-3*p2.y+p3.y)*ttt);                        [smoothPath addLineToPoint:pi];        }                                       [smoothPath addLineToPoint:p2];    }        [smoothPath addLineToPoint:POINT(points.count-1)];        return smoothPath;}@end


0 0
原创粉丝点击