UIVeiw与UIScrollView嵌套,手势/滚动冲突的解决

前言

在项目开发过程中,遇到一个这样的需求:

  • 在视图向上拖动时,使得视图暂时不到顶,而是停留在某个高度处,

  • 此时如果向上拖动,则可以到达顶部

  • 达到顶部后,视图中的子视图才可以滚动(内容足够多)

  • 在向下拖动时,子视图全部展示在顶部时,才允许外部视图向下滚动

具体过程如图:

image

问题

实现这一功能,只能通过UIScrollView的嵌套或者UIView中添加UIScrollView方式(我能想到的,有更好的做法,可以留言指出)

但不管采用哪种方案,有一个重要的问题需要处理:事件冲突

在一般的情况下,只有UIScrollView及其子类才具备滚动的功能,不管是UIScrollView的嵌套还是在普通的UIView中添加UIScrollView,都会出现冲突

方案


采用UIView中添加UICollectionView,然后给UICollectionView添加手势,通过位置,拦截事件响应的对象。

实现

  • 这里需要用到两个重要的概念(非常关键)

1.这个是UIView关于手势事件的拓展方法,返回值为NO时,表示不触发手势事件,该方法在此处运用时,即禁掉自定义添加的拖动手势,响应UICollecionView的滚动手势,我们可以在这个方法中获取到view的当前位置,以及手势的方向,根据这两个因素,就可以决定是否响应事件了
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestureRecognizer;
        if ([self.delegate respondsToSelector:@selector(fetchContainerViewWithStartY)]) {
            /*通过代理获取view的当前位置*/
            CGFloat startY = [self.baseCollectionViewdelegate fetchContainerViewWithStartY];
            CGPoint point = [recognizer translationInView:recognizer.view];//处理方向
            /*
             1.外层view是否在最顶部即frame的y值是否为0(在停止时,y值只有三种情况:0, 150, ScreenHeight-49)
             2.scrollview的偏移值 ( contentOffset.y < 0 偏下  >0 偏上)
             3.滑动方向:向上还是向下 ( point.y > 0:向下, point.y > 0:向上)
             */
if (startY <= 20) {
                if (point.y > 0) {//向下
                    if (self.contentOffset.y > 0) {// <0 偏下(目前的设置,不可能出现)   >0 偏上
                        return YES;
                    } else {
                        return NO;
                    }
                } else {
                    return YES;
                }
            } else {
                return NO;
            }
        }
    }
    return YES;
}

2.是否支持多种手势事件共存,这个也是解决问题的关键,这里需要返回YES,允许支持,我们对UICollectionView添加了自定义的拖动手势以及UICollectionView本身自带了系统的事件
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

  • 在自定义的手势事件中,根据手势的状态,滚动方向以及位置来处理视图的frame,在项目中还配合了一个拖动的小按钮,所以在方法中,还有一个回调的事件


- (void)moveView:(UIPanGestureRecognizer *)recognizer {
    NSLog(@"手势滑动 > recognizer.state : %ld", recognizer.state);
    if (CGRectGetMinY(self.frame) > 20) {
        CGPoint location = [recognizer translationInView:self.superview];
        CGFloat y = location.y + CGRectGetMinY(self.frame);
        if (recognizer.state == UIGestureRecognizerStateBegan) {
            self.startMoveViewFrameY = CGRectGetMinY(self.frame);
        } else if (recognizer.state == UIGestureRecognizerStateChanged) {
            if (y < 20) {
                y = 20;
            } else if (y > SCREEN_HEIGHT - 49.f) {
                y = SCREEN_HEIGHT - 49.f;
            }
            self.frame = CGRectMake(0, y, SCREEN_WIDTH, SCREEN_HEIGHT - 69.f);
            if ([self.delegate respondsToSelector:@selector(scrollWithY:panDirection:animations:)]) {
                //回调处理,拖动的小图标
                [self.delegate scrollWithY:y panDirection:HZPanDirectionNone animations:NO];
            }
        } else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {
            CGPoint locan = [recognizer translationInView:self.collectionView];
            HZPanDirection panDirection = HZPanDirectionNone;
            if (self.startMoveViewFrameY < CGRectGetMinY(self.frame)) {//向下
                y = SCREEN_HEIGHT - 49.f;
                panDirection = HZPanDirectionDown;
            } else {
                panDirection = HZPanDirectionUp;
                if (y < 150) {
                    y = 20;
                } else {
                    y = 150.f;
                }
            }
            [UIView animateWithDuration:1.f animations:^{
                self.frame = CGRectMake(0, y, SCREEN_WIDTH, SCREEN_HEIGHT - 69.f);
            } completion:^(BOOL finished) {
                
            }];
            if ([self.delegate respondsToSelector:@selector(scrollWithY:panDirection:animations:)]) {
                //回调处理,拖动的小图标
                [self.delegate scrollWithY:y panDirection:panDirection animations:YES];
            }
        }
    } else if (CGRectGetMinY(self.frame) == 20) {
        CGPoint point = [recognizer translationInView:recognizer.view];//处理方向
        if (point.y > 0 && self.collectionView.contentOffset.y <= 0) {//向下
            [UIView animateWithDuration:1.f animations:^{
                self.frame = CGRectMake(0, SCREEN_HEIGHT - 49.f, SCREEN_WIDTH, SCREEN_HEIGHT - 69.f);
            } completion:^(BOOL finished) {
                
            }];
            if ([self.delegate respondsToSelector:@selector(scrollWithY:panDirection:animations:)]) {
                //回调处理,拖动的小图标
                [self.delegate scrollWithY:SCREEN_HEIGHT - 49.f panDirection:HZPanDirectionDown animations:YES];
            }
        }
    }
    [recognizer setTranslation:CGPointZero inView:self.superview];
}

这里是阐述比较核心的几个方法,具体的使用,可以下载demo : 传送门

TODO:接下来两周会整理以下模块

  • 自定义UICollectionViewFlowLayout以及转场动画,实现效果
    (图片老是上传失败,直接过地址:http://7qnbrb.com1.z0.glb.clouddn.com/catransition.gif

  • 模块化开发方案示例(每个模块都是能单独运行的工程,cocoapod管理)

  • 采用线程池队列上传文件的解决方案(优势:可以控制上传文件的任务数,避免一张一张上传导致效率不高,也防止开启大量线程同时上传导致线程/内存爆炸💥)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容