视图控件随笔-生活工作点滴iOS面试相关

iOS --- 响应链和事件传递

2019-07-10  本文已影响13人  BabyNeedCare

不知道大家有木有经历过,创建一个按钮在Cell上,但是很多人反馈,点击不了,一点击就走了UITableView的didSelect方法,跳转进去了。

这其实是响应范围只有按钮的位置,UI出来可能就只有这么小点,那我们要怎么做?

可能有小伙伴会说,那就扩大按钮frame啊,多简单的事,立马点击范围就扩大了。

可是。。。如果扩大了,尤其是里面有图片的,一来图片拉伸了,二来可能影响了其他控件的位置。

那能不能变通一下?

那就要了解一下响应链和事件传递了。

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIApplication : UIResponder

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate>

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment>

@interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>

我们都知道继承自UIButton的控件是可以点击,这是因为UIApplication,UIView,UIViewController都继承自UIResponder, UIButton->UIControl->UIView, 最终继承自UIView.

备注一下:很多小伙伴都知道的,查找控件的父视图控件方法. nextResponder, 例如:有button类型的按钮btn, btn. nextResponder, btn. nextResponder的父视图btn. nextResponder. nextResponder...以此类推

事件的分发和传递。

1.当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
2.UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
3.UIWindow将事件向下分发,即UIView。
4.UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
5.遍历子控件,重复以上两步。
6.如果没有找到,那么自己就是事件处理者。如果
7.如果自己不能处理,那么不做任何处理。
其中 UIView不接受事件处理的情况主要有以下三种:
1)alpha <0.01
2)userInteractionEnabled = NO
3.hidden = YES.
这个从父控件到子控件寻找处理事件最合适的view的过程,如果父视图不接受事件处理(上面三种情况),则子视图也不能接收事件。事件只要触摸了就会产生,关键在于是否有最合适的view来处理和接收事件,如果遍历到最后都没有最合适的view来接收事件,则该事件被废弃。

怎么寻找最合适的view

// 此方法返回的View是本次点击事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

// 判断一个点是否落在范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

// 因为所有的视图类都是继承BaseView
- (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] == NO) return nil;
   // 3.从后往前遍历自己的子控件
   NSInteger count = self.subviews.count;
   for (NSInteger i = count - 1; i >= 0; i--) {
       UIView *childView = self.subviews[I];
       // 把当前控件上的坐标系转换成子控件上的坐标系
    CGPoint childP = [self convertPoint:point toView:childView];
      UIView *fitView = [childView hitTest:childP withEvent:event];
       if (fitView) { // 寻找到最合适的view
           return fitView;
       }
   }
   // 循环结束,表示没有比自己更合适的view
   return self;
   }
事件传递给窗口或控件的后,就调用hitTest:withEvent:方法寻找更合适的view,如果子控件是合适的view,则在子控件再调用hitTest:withEvent:查看子控件是不是合适的view,一直遍历,直到找到最合适的view,或者废弃事件。

有个这样的圆形的button:

这样只要在区域内都绝对可以点击成功的。

image.png

那么,如果是这样呢?
这样的点小成这样(只是设极限,现实中会大些,但是也有可能是按钮有图标,实际frame宽高比较小,类似下图)


image.png

1.自定义按钮继承自UIButton

#import "subBtn.h"

@implementation subBtn

// 改变图片的点击范围
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    
    UIBezierPath *path1 = [UIBezierPath bezierPathWithRect:CGRectMake(-20, -20, 200, 200)];

    return [path1 containsPoint:point];
}
@end

2.创建subBtn

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    subBtn *btn = [[subBtn alloc]initWithFrame:CGRectMake(100, 100, 5, 5)];
    
    btn.layer.cornerRadius = btn.frame.size.width / 2;

    btn.layer.masksToBounds = YES;

    btn.backgroundColor = [UIColor grayColor];
    
    [self.view addSubview:btn];
    
    [btn addTarget:self action:@selector(change) forControlEvents:UIControlEventTouchUpInside];
}

- (void)change{
    
    NSLog(@"please go out");
}

扩大区域后,发现, 圆点外也是可以点击的。点击范围扩大了。

上一篇下一篇

猜你喜欢

热点阅读