[iOS]简书个人页效果(上下滚动时菜单悬停且可左右切换)

演示

本文主要介绍 上下滚动时菜单悬停在顶端,并且可以左右滑动切换的特殊视图的实现方式。涉及知识包含事件响应链,UIScorllView滚动模拟,以及刷新控件的基本原理。

一、前言

随着业务的发展,系统提供的常规视图已经难以满足需求,伟大的UI设计师总能想出一些特殊的违反常理的视图来挑战程序员的脑细胞。这种上下滚动还可左右滑动切换最初也不知是哪家提出来的,但是经过这么久发展,这种视图展现方式也被越来越多的App使用,此类开源框架也有不少,实现处理方式也很奇妙,但也各有缺点和限制。

二、部分框架

2.1 YX_UITableView_IN_UITableView

此类视图常见的做法便是UIScorllView套UIScorllView,使内层的UITableView(TAB栏里面)和外层的UITableView同时响应用户的手势滑动事件。当用户从页面顶端从下往上滑动到TAB栏的过程中,使外层的UITableView跟随用户手势滑动,内层的UITableView不跟随手势滑动。当用户继续往上滑动的时候,让外层的UITableView不跟随手势滑动,让内层的UITableView跟随手势滑动。反之从下往上滑动也一样。

缺点:当用户从页面顶端从下往上滑动到TAB栏的过程中,会停住不会有单独scrollview那如丝滑一般的滚动效果。

大部分类似框架都存在该问题,如 MXSegmentedPager也是如此,本来这样效果也挺好了,但是UI说你看看简书就可以,美妆心得就可以,产品说如果没有如丝滑一般的滚动不如不上......

2.2 HHHorizontalPagingView

为了满足UI和产品的需求,笔者辗转反侧 夜不能寐终于发现了曙光,该作者的思路非常巧妙:

HHHorizontalPagingView 通过重写 - (UIView *)hitTest:(CGPoint)point 
withEvent:(UIEvent *)event方法 将headerView 上的响应作用在了 
self.currentScrollView (当前展现的scrollerView)上,滚动就根据contentOffset来移动
headerView。点击就调用 @property (nonatomic, copy) void 
(^clickEventViewsBlock)(UIView *eventView); 
eventView 是hitTest方法查找到的view。

缺点:1.只要headerView稍微复杂点,点击事件就非常难以处理。
     2.破坏了headerView的事件响应链,如果想在headerView上添加轮播图就无法手势左右滑动了。

针对以上两个缺点,缺点2 限于拦截的实现方法导致系统的手势处理都没作用在headerView,已是无法实现。缺点1在笔者冥思苦想下做出了一种解决方案。

点击难以处理主要是,作者为了实现该效果,重写hitTest方法,导致了headerView响应者链条的断裂,虽然作者提供了一个block回调,但对于点击处理无疑是反人类。我的想法是在点击处理时将响应者链条接起来。关于响应者链条可以看看该文章。
Huanhoo 使用@property (nonatomic, copy) void (^clickEventViewsBlock)
(UIView *eventView);来处理点击事件,而eventView就是 命中测试view , 而我要做的
就是通过这个命中测试view向上查找处理该事件。

实现方法:
引入UIView+WhenTappedBlocks这是一个手势处理的分类,
#pragma mark - 模拟响应者链条 由被触发的View 向它的兄弟控件 父控件 延伸查找响应
    - (void)viewWasTappedPoint:(CGPoint)point{
        [self clickOnThePoint:point];
    }
    
    - (BOOL)clickOnThePoint:(CGPoint)point{
        
        if ([self.superview isKindOfClass:[UIWindow class]]) {
            return NO;
        }
        
        if (self.block) {
            self.block();
            return YES;
        }
        
        __block BOOL click = NO;
        // 看兄弟控件
        [self.superview.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            // 转换坐标系 看点是否在View上
            CGPoint objPoint = [obj convertPoint:point fromView:self];
            if (!CGRectContainsPoint(obj.frame, objPoint)) {
                //            NSLog(@"-----%@",NSStringFromCGPoint(objPoint));
                return;
            }
            if (self.block) {
                self.block();
                click = YES;
                *stop = YES;
            }
        }];
        
        if (!click) {
            return [self.superview clickOnThePoint:point];
        }
        
        return click;
    }
    
正常响应,有点击手势触发方法来执行block,非正常点击 主动调用
- (void)viewWasTappedPoint:(CGPoint)point;方法就可以接起响应者链条。

关于以上的实现可以看 JYHHHorizontalPagingView 1.1.0版本这是笔者对
HHHorizontalPagingView的一个优化处理让点击易于处理,但是也仅仅只能接起点击事件。在headerView上加轮播图无法实现,甚至UIButton的长按高亮效果在headerView上也会失效。但是它有丝滑一般的滑动,headerView和下部的每一个ScrollView滚动效果都是一体的。

三、 JYHHHorizontalPagingView模拟ScrollView的滚动

上面介绍的框架限于实现方式都各有缺陷,那到底能不能做到完美,答案是肯定的,毕竟美妆心得做到了,简书做到了(UIButton 的长按高亮效果犹在说明headerView上的响应并没有被破环)。我的思路是在headerView上添加拖拽手势改变下方scrollView的contentOffset,模拟scrollView的减速滑动以及弹簧效果。

3.1模拟弹簧效果
  弹簧效果的实现很简单使用UIView 动画即可。
        [UIView animateWithDuration:0.35 animations:^{
            self.currentScrollView.contentOffset = CGPointMake(contentOffset.x, border);
            [self layoutIfNeeded];
        }];
3.2模拟减速滑动效果

减速滑动效果确实不好实现,我尝试过不少方法效果都不太好,后来看到了饿了么一位开发者的博客,他是通过UIDynamic的物理特性来模拟scrollView滚动。按照他的方法完美实现了模拟。作者的博客地址目前好像无法进去就贴一个推酷的转载用UIKit Dynamics模仿UIScrollView,具体的一些说明作者讲的很清晰我就不多说了,大家可以自己看看,也可以直接看我的代码。

四、扩展功能

到目前为止,该类视图的功能可以说是相当完美了,但是需求永远是难以满足的,某天产品说现在数据没有更新机制只能上拉刷新,不能下拉刷新。what?这么反人类的功能你还要加下拉刷新......

针对此类需求,笔者为此添加了单独下拉刷新以及整体下拉刷新,由于篇幅问题,笔者就不再多说了,感兴趣的同学可以去 github -JYHHHorizontalPagingView看具体介绍说明。

五、结尾

如果我的文章对你有帮助或者给了你一些启发,希望你能在github给个小星星,如果你在使用过程中遇到了Bug请留言反馈,我会及时解决。欢迎转载(在文章开头标明来源即可)。

六、补充

1.很多人反馈有偏移啥的,看下 self.edgesForExtendedLayout = UIRectEdgeNone;

  1. pod 1.2.1 版本已经支持自定义 SegmentView,自定义view只需支持JYSegmentViewProtocol协议即可。具体可参考默认的JYSegmentView 。

七、最后再推荐一个(相当给力,可定制性强)

github -HVScrollView
感谢 S型身材的猪

思路:
首先最底部是一个全屏的scrollView,这个scrollView的作用是横向滑动,scrollView上面添加若干个tableView,然后每个tableView上设置顶部内边距,顶部由内边距空出来的地方就放headerView和菜单栏。headerView和菜单栏放在控制器 view上。当滑动tableView时,让headerView的y值随着 scrollview的偏移量时刻改变,刷新也不会有问题。

我看了下代码,相当给力,思路实现都相当好值得学习。
大家看评论的话,作者有这么一句话:
不知道为什么网上几个人封装都去用collectionView搞那么复杂。

为什么这么复杂呢?一个功能又是响应链的打断,又是模拟滚动,view还一层套一层。针对一种特殊需求的出现,前期并没有太多的开源代码供大家学习和研究,在时间的紧逼之下,大家都选择了自己的处理方式,随着后期的迭代,为了达到需要的效果,各个流派在原有基础上做出了相应处理。这些方案也许并不是最好的,最简单的,但它们都包含了作者的智慧与思想。对于这些作者我们应心存感激,因为他们开源,他们无私的分享了自己的思路,这些如今看来也许并不友好的实现方式却是在我们最迷茫最需要时给了我们帮助与启发。也正因为有他们的方案来保证项目的正常上线,我们才敢尝试更简单更高效的方法。最后感谢开源。

本文所列举的一些DEMO都有自己的实现方式,大家可研究学习一下选择最适合的。感谢这些开源作者。

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

推荐阅读更多精彩内容

  • 为关注的人点赞
    346e82fe5086阅读 121评论 0 0
  • 路边乞丐弹吉他, 人人听了人人夸。 不知你在笑个啥, 难 道你还有才华。
    晚风轻语阅读 153评论 1 10
  • 周幽王在位时,西周国势衰落,社会矛盾尖锐,潜伏着政治和经济的诸多危机。但他不思进取,一味贪求淫荡奢靡的生活。他为博...
    顾晴晞阅读 320评论 0 0
  • 知己图片发自肉肉菜菜 作者肉肉菜菜 有与无,有区别吗? 一个是属于自己, 一个是属于别人, 这就是区别。 知己又是...
    南末森森书卷记阅读 838评论 4 24
  • 我与宝爸2008年年底,匆匆走进婚姻,没有共同话题,没有居住的小窝,没有婚礼。婚后,我们与父母住一起,来自新化...
    7be5afaf5ed4阅读 229评论 0 0