实现微博个人页面的滑块浮动切换页面效果(OC)

前言

这篇文章真的是不想写的, 因为之前分享过一篇用swift实现的相同的效果, 无奈很多的朋友总是问有没有oc版的, 好吧, 也可以理解在swift不太普及的情况下出现这样的事情, 看来最近真的是闲的慌, 每天在交流群里跟大家吹牛不说, 还把这个用oc写了一遍, 不过实现的过程和原来的swift有一点不一样, 因为对应的oc版的ZJScrollPageView更新了, 但是swift的并没有更新.demo地址

最终效果


示例效果.gif


一` 实现的思路
, 大多和swift中一样只是增加了一个scrollView

构思: 利用监控和设置UIScrollView的偏移量来实现


层级结构

.

  1. 层级结构, 第一层为控制器的view, 第二层为containerView, 第三层为contentView, 第四层为 headView(为了实现header也能滑动, 添加了一个scrollView在他的下面)和segmentView

  2. 第三层的contentView,设置contentView的大小和控制器的view的大小一样, 来显示子控制器的view的内容, 需要设置他的子控制器view的tableView(collectionVie)的初始偏移量为segmentView和headView的高度之和,同时将segmentView和headView添加到view上面, 以实现tableView在滚动的时候segmentView和headView可以同步滚动

  3. subview的添加顺序, 先添加contentView, 然后再添加segmentView和headView(因为contentView和view大小一样, 若是在后面添加就将segmentView和headView遮住了)

  4. 同步滚动原理: 监控子控制器的scrollView的滚动偏移量(contentOffset.y), 根据偏移量的改变量来同步的调整segmentView和headView的frame, 这里的监控之前我是使用Closure来实现,但是后面需要更新tableView的偏移量有需要一个Closure, 所以就改为了delegate来实现.

  5. 使segmentView浮动: 通过监控子控制器的scrollView的滚动偏移量(contentOffset.y), 根据偏移量的改变量来同步的调整segmentView和headView的frame, 当segmentView同步"滚动"到顶部的时候, 通过判断scrollView的滚动偏移量的范围来固定segmentView和headView的frame, 即达到浮动效果

  6. 在segmentView和headView的frame随着scrollView滚动到下方原始位置的时候, 通过判断scrollView的滚动偏移量的范围来固定segmentView和headView的frame

二. 实现部分 .

  1. 这些懒加载中设置了具体的ZJScrollPageView的属性和初始化的frame

    Snip20160706_7.png

2, #pragma ZJScrollPageViewDelegate 代理方法

- (NSInteger)numberOfChildViewControllers {
    return self.titles.count;
}

- (UIViewController<ZJScrollPageViewChildVcDelegate> *)childViewController:(UIViewController<ZJScrollPageViewChildVcDelegate> *)reuseViewController forIndex:(NSInteger)index {
    UIViewController<ZJScrollPageViewChildVcDelegate> *childVc = reuseViewController;

    if (!childVc) {
        childVc = [[ZJPageViewController alloc] init];

    }


    if (index%2==0) {
        childVc.view.backgroundColor = [UIColor blueColor];
    } else {
        childVc.view.backgroundColor = [UIColor redColor];

    }
    // 设置代理, 用于处理子控制器的滚动
    _currentChildVc = (ZJPageViewController *)childVc;
    _currentChildVc.delegate = self;
    return childVc;
}

3, 相应子控制器的scrollView的滚动, 在这里面调整segmentView等的位置,
这里面会处理三种情况,

  • 第一种是向上滚到滑块的位置在navigationBar的时候, 需要使得继续向上滚的时候滑块停在这里
  • 第二种是向下滚动直到headView完全显示出来的时候, 需要处理
  • 第三种就是上面两种之间的时候, 需要让segmentView等同步滚动
  • 下面的代码就是分别处理这三种情况
- (void)scrollViewIsScrolling:(UIScrollView *)scrollView {
    _childOffsetY = scrollView.contentOffset.y;
    self.currentOffsetY = _childOffsetY + defaultOffSetY;

//    NSLog(@"%f", _currentOffsetY);

    if (self.currentOffsetY <= 0 ) {

// 让headView停在navigationBar下面
        self.segmentView.zj_y = -_childOffsetY - segmentViewHeight;
        self.scrollView.zj_y = self.segmentView.zj_y - headViewHeight;

    }
    else if (self.currentOffsetY>=headViewHeight) {
        // 使滑块停在navigationBar下面
        self.scrollView.zj_y = naviBarHeight - headViewHeight;
        self.segmentView.zj_y = naviBarHeight;

    }

    else {
        // 这里是让滑块和headView随着上下滚动
        self.segmentView.zj_y = -_childOffsetY - segmentViewHeight;
        self.scrollView.zj_y = self.segmentView.zj_y - headViewHeight;
        // "递归"
        if (self.scrollView.contentOffset.y == self.currentOffsetY) {
            return;
        }
        [self.scrollView setContentOffset:CGPointMake(0, self.currentOffsetY)];

    }

}

4, 处理headView的滚动

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    self.currentOffsetY = scrollView.contentOffset.y;
// 这样的方式达到, 底部的scrollView滚动,但是headView在屏幕上的位置不变, 
    self.headView.zj_y = self.currentOffsetY;

    if (self.currentOffsetY < 0) {
        self.containerView.zj_y = -self.currentOffsetY;
        return;
    } else {
        self.containerView.zj_y = 0;

    }

    if (_currentChildVc.tableView.contentOffset.y == self.currentOffsetY - defaultOffSetY) {
        return;
    }
  // 同步滚动子控制器的scrollView
    [_currentChildVc.tableView setContentOffset:CGPointMake(0, self.currentOffsetY - defaultOffSetY)];
}

5, 当切换页面的时候, 需要根据当前页面的scrollView的偏移量以及当前的segmentView等的位置, 来整体的调整, 同时在悬停的时候保留每个页面的滚动偏移量, 这里处理两种情况

  • 新页面出来的时候滑块并没有到上面悬停的位置, 这个时候, 将新页面的scrollView偏移量置为初始的
  • 新页面出来的时候滑块在上面悬停的位置, 这个时候, 使用新页面的scrollView偏移量, 并且调整各个控件的位置
- (void)setupScrollViewOffSetYWhenViewWillAppear:(UIScrollView *)scrollView {

    dispatch_block_t setHeight = ^ {
        _childOffsetY = scrollView.contentOffset.y;
        [scrollView setContentSize:CGSizeMake(0, MAX(scrollView.bounds.size.height - naviBarHeight - segmentViewHeight, scrollView.contentSize.height))];
    };

    if (_childOffsetY < -(naviBarHeight + segmentViewHeight)) {
        [scrollView setContentOffset:CGPointMake(0, _childOffsetY)];
        setHeight();
        return;
    } else {
        if (scrollView.contentOffset.y < -(naviBarHeight + segmentViewHeight)) {
            [scrollView setContentOffset:CGPointMake(0, -(naviBarHeight + segmentViewHeight))];

            // 使滑块停在navigationBar下面
            self.scrollView.zj_y = naviBarHeight - headViewHeight;
            self.segmentView.zj_y = naviBarHeight;
            setHeight();
            return;
        }

        setHeight();
        return;

    }

}

6, 子控制器中, 需要使用代理, 通知, kvo, block等方式让父控制器知道当前的scrollView(tableView, collectionView均可)的偏移量即可, 这里我使用了代理来实现

#pragma ZJScrollPageViewChildVcDelegate
// 每次页面出现的时候会调用, 这个时候传递当前的偏移量
- (void)setUpWhenViewWillAppearForTitle:(NSString *)title forIndex:(NSInteger)index firstTimeAppear: (BOOL)isFirstTime {
    [self.delegate setupScrollViewOffSetYWhenViewWillAppear:self.tableView];

    if(isFirstTime) {
        // 加载数据
    } else {
        //刷新...
    }
}
#pragma UIScrollViewDelegate 传递滚动的偏移量
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self.delegate scrollViewIsScrolling:scrollView];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellId];
// 这里设置初始的内容便宜 200+64+44 这个数字是父控制器中的常量, 这里偷个懒
    [self.tableView setContentInset:UIEdgeInsetsMake(200+64+44, 0, 0, 0)];
    [self.view addSubview:self.tableView];
}
然后呢就简单的实现了这种效果, 其中可能会有问题, 欢迎交流, demo地址


文/ZeroJ(简书作者)
原文链接:http://www.jianshu.com/p/e6d2bd624e85
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值