项目渐渐变大的过程当中,因为ViewController 承担的业务逻辑愈来愈多,会遇到ViewController膨胀变得庞大无比的状况。好比在我作的项目中,有一个页面包含了展现信息业务、弹幕业务、其余附加业务,致使这个页面的ViewController超过了1000行,这么庞大的一个类,要修改东西第一个遇到的问题就是要查找定位,修改还很可能还会影响到其余地方,因此打算把这些模块独立出来,作成独立的ViewController,再在一个主要的ViewController中引入其余业务的ViewController。
遇到了一个问题是ViewController中的View事件透传的问题,加入了多个ViewController的View,这些View若是须要交互,就须要把View的userInteractionEnabled属性设置为YES,这样致使了事件会被顶部的View拦截,底下的View事件会接收不到。ios
主要是使用了 hitTest: withEvent:
作了一个处理,对于当前View须要处理交互事件的子View,正常处理当前子View的事件,遇到空白的地方,则把事件继续传递到底下的View作处理,其余的页面也按照这种方式处理。git
Demo: Demoapp
hitTest: withEvent:
的做用就是返回一个能够处理用户事件的View,有如下几种状况ide
UIControl
类型,则会调用对应的targetbeginTrackingWithTouch: withEvent:
等方法touchesBegan: withEvent
等方法。hitTest: withEvent:
返回一个nil则事件会传递到下一层作一样的处理。下面是自定义的UIView, hitTest: withEvent:
处理须要交互的子View,若是点击的点位置不是须要加护的View的位置,返回nil,交个下一层处理。atom
@interface PTTransparentView() {} @property (nonatomic, strong) NSMutableArray* hitViews; @end @implementation PTTransparentView /** 把须要交互的View添加到当前View中 */ - (void)addTouchableView:(UIView*)touchableView { if (touchableView) { [self.hitViews addObject:touchableView]; } } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { for (int i = 0; i<self.hitViews.count; i++) { UIView* hitView = self.hitViews[i]; CGRect mappedFrame = [hitView convertRect:hitView.bounds toView:self]; if (CGRectContainsPoint(mappedFrame, point)) { return hitView; } } return nil; } - (NSMutableArray *)hitViews { if (!_hitViews) { _hitViews = [[NSMutableArray alloc] initWithCapacity:2]; } return _hitViews; } @end
自定义的UIView在ViewController中的使用,spa
loadView
初始化 self.view。示例代码以下:code
#import "AViewController.h" #import "PTTransparentView.h" @interface AViewController () @property (nonatomic, strong) UIButton* helpButton; @end @implementation AViewController - (void)loadView { self.view = [[PTTransparentView alloc] initWithFrame:[UIScreen mainScreen].bounds]; } - (void)viewDidLoad { [super viewDidLoad]; self.view.userInteractionEnabled = YES; [self.view addSubview:self.helpButton]; // 添加可点击的View if ([self.view isKindOfClass:[PTTransparentView class]]) { [((PTTransparentView*)self.view) addTouchableView:self.helpButton]; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (UIButton *)helpButton { if (!_helpButton) { _helpButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; _helpButton.frame = CGRectMake(100, 100, 100, 40); [_helpButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; [_helpButton setTitle:@"A帮助" forState:UIControlStateNormal]; [_helpButton addTarget:self action:@selector(onHelpBtnClick) forControlEvents:UIControlEventTouchUpInside]; } return _helpButton; } - (void)onHelpBtnClick { NSLog(@"====A帮助"); } @end
在容器的ViewController添加了 AViewController 和 BViewController 对应的view,AViewController 和 BViewController 中各包含了一个可交互的按钮,点击按钮的位置处理按钮的点击事件,点击空白位置则不处理事件,事件会传递到下一层,若是点击的不是按钮的位置,最终事件会在容器的ViewController中的 touchesBegan: withEvent:
被处理。orm
@interface ViewController () @property (nonatomic, strong) AViewController* aVC; @property (nonatomic, strong) BViewController* bVC; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.aVC.view]; self.aVC.view.frame = self.view.bounds; [self.view addSubview:self.bVC.view]; self.bVC.view.frame = self.view.bounds; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (AViewController *)aVC { if (!_aVC) { _aVC = [AViewController new]; } return _aVC; } - (BViewController *)bVC { if (!_bVC) { _bVC = [BViewController new]; } return _bVC; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"touchesBegan===="); } @end
以上就是UIView事件传递处理的一个简单应用场景。事件