iOS事件机制(点击、手势、UIControl)

iOS事件机制(点击、手势、UIControl),第1张

注意:对于一个手指的触摸,是UITouch每次状态改变的时候都会回调UIResponder相对应的处理方法。对于多个手指的触摸,也许多个UITouch状态的改变一起回调UIResponder的处理方法,也许每个UITouch状态的改变都会回调UIResponder的处理方法,例如,两个点击,可能只有一个touchesBegan的回调,两个touchesEnded的回调.同时,多个UIControl状态改变只有一次touchesBegan等方法回调的参数touches里touch的个数我测试的时候只有一个,不要以为所有状态改变的UITouch只有一次回调时都会放到touches参数里。关于多点触摸的处理个人不建议在UITouch的响应机制里去做处理,里面具体原理并不明朗,实际开发中的借鉴也不多,涉及多点触摸使用手势更好。

UIResponder是iOS中用于处理用户事件的API,可以处理触摸事件、按压事件(3D touch)、远程控制事件、硬件运动事件。可以通过touchesBegan、pressesBegan、motionBegan、remoteControlReceivedWithEvent等方法,获取到对应的回调消息。UIResponder不只用来接收事件,还可以处理和传递对应的事件,如果当前响应者不能处理,则转发给其他合适的响应者处理。

应用程序通过响应者来接收和处理事件,响应者可以是继承自UIResponder的任何子类,例如UIView、UIViewController、UIApplication等。当事件来到时,系统会将事件传递给合适的响应者,并且将其成为第一响应者。

第一响应者未处理的事件,将会在响应者链中进行传递,传递规则由UIResponder的nextResponder决定,可以通过重写该属性来决定传递规则。当一个事件到来时,第一响应者没有接收消息,则顺着响应者链向后传递。

Gesture Recognizer 是对底层事件处理的封装,是为了让使用者能够更简单处理事件。

手势分为离散型手势(discrete gestures)和持续型手势(continuous gesture)。

手势响应过程:

手势状态:

UIControl是系统提供的能够以target-action模式处理触摸事件的控件,iOS中UIButton、UISegmentedControl、UISwitch等控件都是UIControl的子类。

值得注意的是,UIConotrol是UIView的子类,因此本身也具备UIResponder应有的身份。

UIControl作为控件类的基类,它是一个抽象基类,我们不能直接使用UIControl类来实例化控件,它只是为控件子类定义一些通用的接口,并提供一些基础实现,以在事件发生时,预处理这些消息并将它们发送到指定目标对象上。

UIControl的触发过程:

四个重要识别方法是在touchesBegan、touchesMoved、touchedEnded、touchesCancelled里回调的。

推测是:endTrackingWithTouch调用后识别了行为,做标记,返回到touchesEnded后,判断本UIControl是否易识别行为,调用行为回调。

App接收到触摸事件后,会被放入当前应用程序的UIApplication维护的事件队列中.

由于事件一次只有一个,但是能够响应的事件的响应者众多,所以这就存在一个寻找第一响应者的过程。

调用方法,获取到被点击的视图,也就是第一响应者。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

hitTest:withEvent:方法内部会通过调用pointInside:这个方法,来判断点击区域是否在视图上,是则返回YES,不是则返回NO。

经过Hit-Testing的过程后,UIApplication已经知道了第一响应者是谁,接下来要做的事情就是:

自定义的view的touchesBegan、touchesMoved、touchesEnded、touchedCancelled四个方法重写,记录打印过程,该view上添加tapGestureRecognized手势,该tapGestureRecognized也覆写了这四个方法。

点击view调用打印过程输出:

调用栈:

结合上面的输出和调用栈,我们可能并不能明确的看出有手势的时候点击的过程,不过如果你自己调试,是能得出如下结论的:

UIGestureRecognizer和UITouch的关系可以由UIGestureRecognizer的三个属性影响:cancelsTouchesInView、delaysTouchesBegan、delaysTouchesEnded。

本身就是在UIResponder的UITouchesBegan、UITouchesMoved、UITouchedEnded、UITouchesCancel四个回调中调用的。

UIControl的响应处理并不会影响UIResponder的响应链的处理,但是UIControl会影响另一个UIControl,子视图的UIControl具有优先级。

UIGestureRecognizer和UIControl并没有决定的优先级。

从iOS6开始在控件的父视图上面添加相应的手势,控件就会控制阻止手势行为,比如:

tap 手势在 UIButton,UISwitch,UIStepper,UISegmentControl,UIPageControl;

swipe 手势在 UISlider;

pan 手势在 UISwitch;

其他可能是手势优于控件的行为。

UIResponder有touchesBegan等四个方法,默认向superview传递。

所有需要自定义点击处理逻辑的UIResponder子类要覆盖这四个方法。

点击事件由四个方法处理。

UIButton的处理也是需要经过这四个方法。

UIGestureRecognizer也有touchesBegan等四个方法。

手势不在响应链里,但是也会观察它的view和subView的点击。

UIGestureRecognizer会影响UIResponder的四个响应点击的方法。

默认点击事件响应关键步骤说明:

1)用户手指点击屏幕,经过系统传递到UIApplication, UIApplication通过hitTest:方法找到对应UITouch发生的第一响应者view

2)UIApplication更新手势状态,从第一响应者上的手势到其视图层上所有先辈视图上的手势都会接收这个UITouch来更新手势状态

3)UIApplication将UITouch交给找到的第一响应着view处理

4)UIApplication更新手势状态,识别成功后,会向UITouch的第一响应者发送cancel方法

加上UIControl会让过程变得复杂,关于UIControl的原理,不清楚,也不敢妄下结论,依据网上和实际测试大致推断:

1)它不会影响UITouch本身的响应流程,但是会影响其他UIControl和UIGestureRecognizer的响应

2)自定义的UIControl是和UITouch本身的响应过程是一样的

3)系统定义的UIControl和UIGestureRecognizer同一个优先级,谁先识别出来,另一个就out了,但是UIControl和UIGestureRecognizer有一点不同,它并不会cancel UITouch的流程。

关于UITouch、UIGestureRecognizer、UIControl之间影响说明:

1)UITouch和UIGestureRecognizer:UIGestureRecognizer优先级高于UITouch,由UIGestureRecognizer的三个参数cancelsTouchesInView、delaysTouchesBegan、delaysTouchesEnded决定对UITouch的影响,默认情况下,UIGestureRecognizer识别成功后,会向UITouch发送cancel

避免:

1)尽量不要覆盖重写UIResponder的touchesBegin、touchesMoved、touchesCancelled、touchesEnded这四个方法,如果需要覆盖重写,逻辑应该尽量简单,不宜做复杂的处理,

2)不要自定义UIControl,直接使用系统定义的UIControl

3)UIControl上不要添加UIControl子视图

4)不要依赖UIGestureRecognizer的delayTouchBegin和delayTouchEnded

5)不要自定义UIGestureRecognizer

参考文章:

1) iOS 事件(UITouch、UIControl、UIGestureRecognizer)传递机制

2) Touch Event Handing 教学 — part 1

这个工具类应该是一个直接或间接继承自UIVIew的自定义控件,如果要实现如上的效果,使用手势就可以很轻松的实现。对于这种上滑或下拉的手势你可以选择UISwipeGestureRecognizer 来处理。再加上动画效果,根据上滑或下拉来修改这个控件的 postion y 的值就可以了

     UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bgImage"]] 

    创建并设置默认图, 也可以

    UIImageView*imageView = [[UIImageView alloc] init]

    imageView.image= [UIImageimageNamed:@"bgImage"]

    还可以这样先设置imageview的大, 在设置图片

     UIImageView*imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(0,144,SCREEN_Width,50))]

    imageView.image= [UIImageimageNamed:@"bgImage"]

    由此可看imageview的frame可以这样设置

     imageView.frame=CGRectMake(0,144,SCREEN_Width,50)

    通常我们使用的的imageview都会添加圆角边框

     imageView.layer.masksToBounds = YES

    imageView.layer.cornerRadius=25

    imageView.layer.borderColor = [UIColor blueColor].CGColor

    imageView.layer.borderWidth=1

    这个圆角和边框像view和label以及button的设置方式都是一样的 当然imageview也一样

     imageView.backgroundColor= [UIColorclearColor]图片设置背景颜色, 我通常使用clearColor  透明

     imageView.userInteractionEnabled = YES图片设置成可交互, 设置为NO则不能交互

     [self.viewaddSubview: imageView]添加视图也可叫做显示视图

    设置图片内容的布局方式 imageView.contentMode

    这个属性是用来设置图片的显示方式,如居中、居右,是否缩放等

    imageView.contentMode = UIViewContentModeScaleAspectFit

     UIViewContentMode contentMode枚举类型

        (1)  UIViewContentModeScaleToFill    默认,对图片进行拉伸处理(不是按比例),是充满bouns

        (2)  UIViewContentModeScaleAspectFit    按原图比例进行拉伸,是图片完全展示在bouns中

        (3)  UIViewContentModeScaleAspectFill    按原图比例填充,使图片展示在bouns中,可能只显示部分

        (4)  UIViewContentModeRedraw    重划边界变化(重设 - setNeedsDisplay)

        (5)  UIViewContentModeCenter    图片显示在imageview的正中间,原图大小

        (6)  UIViewContentModeTop    图片显示在imageview的上部,原图大小

        (7)  UIViewContentModeBottom    图片显示在imageview的下部,原图大小

        (8)  UIViewContentModeLeft    图片显示在imageview的左部,原图大小

        (9)  UIViewContentModeRight    图片显示在imageview的右部,原图大小

        (10)  UIViewContentModeTopLeft    图片显示在imageview的左上部,原图大小

        (11)  UIViewContentModeTopRight    图片显示在imageview的右上部,原图大小

        (12)  UIViewContentModeBottomLeft    图片显示在imageview的左下部,原图大小

        (13)  UIViewContentModeBottomRight    图片显示在imageview的右下部,原图大小

     imageView.alpha = 1.0   设置图片透明度

        NSString *path1 = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"jpg"]

        NSString *path2 = [[NSBundle mainBundle] pathForResource:@"2" ofType:@"jpg"]

        NSString *path3 = [[NSBundle mainBundle] pathForResource:@"3" ofType:@"jpg"]

        imageView.animationImages = @[[UIImage imageWithContentsOfFile:path1],[UIImage imageWithContentsOfFile:path2],[UIImage imageWithContentsOfFile:path3]]

        imageView.animationDuration = 5.0f   设置循环一次的时间

        imageView.animationRepeatCount = 0    // 设置循环次数(0为无线循环)

        [imageView startAnimating]            // 开始动画

        [imageView stopAnimating]              // 停止动画

    NSData *imageData = [NSData dataWithContentsOfFile:path]

    UIImage *image4 = [UIImage imageWithData:imageData]

    NSString *path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"jpg"]

    UIImage *image2 = [UIImage imageWithContentsOfFile:path]

    ImageView.hidden = NO    隐藏或者显示图片 YES为隐藏

    [ImageView sizeToFit]    将图片尺寸调整为与内容图片相同

    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapImageView:)] // 设置手势

  [ImageView addGestureRecognizer:singleTap] // 给图片添加手势


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/bake/11570562.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-17
下一篇 2023-05-17

发表评论

登录后才能评论

评论列表(0条)

保存