簡單說下關於開發中容易遇到的父試圖添加手勢與子試圖點擊事件衝突,UIScrollView 嵌套 UIScrollView 、 UIScrollView 嵌套 UITableView的情況手勢衝突問題; 點擊衝突 如果給現有的基於 UIView 的 xkTestView 上加一個點擊手勢 gestTa ...
簡單說下關於開發中容易遇到的父試圖添加手勢與子試圖點擊事件衝突,UIScrollView 嵌套 UIScrollView 、 UIScrollView 嵌套 UITableView的情況手勢衝突問題;
點擊衝突
如果給現有的基於 UIView 的 xkTestView 上加一個點擊手勢 gestTap,然後在 xkTestView 中間區域添加一個 tableview,我們想響應 gestTap,同時也想響應 tableview 的 cell 點擊代理事件,這時可以添加 gestTap 點擊手勢代理:
<UIGestureRecognizerDelegate>
然後在點擊事件代理方法中實現
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { if ([NSStringFromClass([touch.view class]) isEqualToString:@"xkTestView"]) { return YES; } return NO; }
scrollView 嵌套 tableView 類衝突
這裡直接用 scrollView 嵌套 tableView 來處理下滑動時的手勢衝突問題,其實蘋果並不建議我們這樣做,但是在實際項目中,有些需求會經常用嵌套來實現,在什麼情況下滑動 tableView 不滑動 scrollView,什麼情況下滑動 scrollView 不滑動 tableView,其實如果做其他的嵌套都是一樣的,先看下最終效果圖:
1)首先新建一個基於 UIScrollView 的 XKBaseScrollView ,並實現 <UIGestureRecognizerDelegate> 代理,XKBaseScrollView 用做主父試圖來添加子試圖內容
XKBaseScrollView.h
#import <UIKit/UIKit.h> @interface XKBaseScrollView : UIScrollView <UIGestureRecognizerDelegate> @end
XKBaseScrollView.m
#import "XKBaseScrollView.h" @implementation XKBaseScrollView //是否支持多時候觸發,這裡返回YES -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES; } @end
2)然後新建一個基於 UITableView 的 XKTargetTableView ,並實現 <UIGestureRecognizerDelegate,UITableViewDelegate,UITableViewDataSource> 代理
XKTargetTableView.h
#import <UIKit/UIKit.h> @interface XKTargetTableView : UITableView ///可否滑動 @property (nonatomic,assign) BOOL canSlide; ///滑動block通知 @property (nonatomic,copy) void (^slideDragBlock)(void); @end
XKTargetTableView.m
#import "XKTargetTableView.h" @interface XKTargetTableView ()<UIGestureRecognizerDelegate,UITableViewDelegate,UITableViewDataSource> @property (nonatomic,assign) CGFloat currOffsetY; @end @implementation XKTargetTableView - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{ self = [super initWithFrame:frame style:style]; if (self) { self.backgroundColor = [UIColor whiteColor]; self.delegate = self; self.dataSource = self; self.tableFooterView = [UIView new]; [self registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"]; } return self; } //是否支持多時候觸發,這裡返回YES - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES; } #pragma mark ========== tableView 代理 ========== - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 20; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 50; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row]; return cell; } #pragma mark ========== scrollview 代理 ========== - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ _currOffsetY = scrollView.contentOffset.y; } -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ if (!self.canSlide) { scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y == 0 ? 0 : _currOffsetY); } _currOffsetY = scrollView.contentOffset.y; if (scrollView.contentOffset.y < 0 ) { self.canSlide = NO; scrollView.contentOffset = CGPointZero; //到頂通知父視圖改變狀態 if (self.slideDragBlock) { self.slideDragBlock(); } } scrollView.showsVerticalScrollIndicator = self.canSlide ? YES : NO; } @end
3)最後在使用的 ViewController 中實現
#import "ViewController.h" #import <SDAutoLayout.h> #import "XKBaseScrollView.h" #import "XKTargetTableView.h" @interface ViewController ()<UIScrollViewDelegate> ///容器 @property (nonatomic,strong) XKBaseScrollView *scrollView; @property (nonatomic,strong) XKTargetTableView *tableView; ///是否可以滑動 scrollView @property (nonatomic,assign) BOOL canSlide; @property (nonatomic,assign) CGFloat lastPositionY; ///滑動臨界範圍值 @property (nonatomic,assign) CGFloat dragCriticalY; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _dragCriticalY = 200; [self.view addSubview:self.scrollView]; self.scrollView.sd_layout. topSpaceToView(self.view, 0). leftSpaceToView(self.view, 0). rightSpaceToView(self.view, 0). bottomSpaceToView(self.view, 0); [self.scrollView setupAutoContentSizeWithBottomView:self.tableView bottomMargin:0]; __weak __typeof__(self) weekSelf = self; self.tableView.slideDragBlock = ^{ weekSelf.canSlide = YES; weekSelf.tableView.canSlide = NO; }; } -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ CGFloat currentPostion = scrollView.contentOffset.y; /* 當 底層滾動式圖滾動到指定位置時, 停止滾動,開始滾動子視圖 */ if (currentPostion >= self.dragCriticalY) { scrollView.contentOffset = CGPointMake(0, self.dragCriticalY); if (self.canSlide) { self.canSlide = NO; self.tableView.canSlide = YES; } else{ if (_lastPositionY - currentPostion > 0){ if (self.tableView.contentOffset.y > 0) { self.tableView.canSlide = YES; self.canSlide = NO; } else{ self.tableView.canSlide = NO; self.canSlide = YES; } } } }else{ if (!self.canSlide && scrollView.contentOffset.y == self.dragCriticalY ) { scrollView.contentOffset = CGPointMake(0, self.dragCriticalY); } else{ if (self.tableView.canSlide && self.tableView.contentOffset.y != 0) { scrollView.contentOffset = CGPointMake(0, self.dragCriticalY); } else{ } } } _lastPositionY = currentPostion; } - (XKBaseScrollView *)scrollView{ if (!_scrollView) { _scrollView = [[XKBaseScrollView alloc]init]; _scrollView.showsVerticalScrollIndicator = NO; _scrollView.delegate = self; _scrollView.backgroundColor = [UIColor redColor]; UIView *view = [[UIView alloc]init]; view.backgroundColor = [UIColor blueColor]; [_scrollView addSubview:view]; view.sd_layout. topSpaceToView(_scrollView, 0). leftSpaceToView(_scrollView, 0). rightSpaceToView(_scrollView, 0). heightIs(300); [_scrollView addSubview:self.tableView]; self.tableView.sd_layout. topSpaceToView(view, 0). leftSpaceToView(_scrollView, 0). rightSpaceToView(_scrollView, 0). heightIs(self.view.bounds.size.height - (300 - self.dragCriticalY)); } return _scrollView; } - (XKTargetTableView *)tableView{ if(!_tableView){ _tableView = [[XKTargetTableView alloc]initWithFrame:CGRectZero style:UITableViewStylePlain]; } return _tableView; } @end
註:此 demo 需引用 SDAutoLayout