美文网首页iOSui控件和效果的实现自己的swift
实现微博个人页面的滑块浮动切换页面效果

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

作者: ZeroJ | 来源:发表于2016-04-21 22:23 被阅读2776次

    前言, 之前写了一篇文章, 实现类似淘宝商品界面的滑块效果, 不过很(##抱歉##)的是, 笔者当时没有测试TableView等的使用,大家反映TableView不能正常使用,(研究一段时间发现, 之前的做法不适结合合TableView使用) 这里就整理了下重新实现了, 比之前的确实要麻烦一点, 如果在你的项目中不需要结合TableView就可以简单的按照之前的方法实现.

    最终效果

    示例效果.gif

    注意!!! 实现这个效果github上的代码有更新, 大家使用最新的代码

    更新说明: 使每个页面在顶端滚动的偏移量不相互影响

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

    层级结构

    .

    1. 层级结构, 第一层为控制器的view, 第二层为contentView, 第三层为 headView和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 属性部分, 这些懒加载方法里面设置了他们的相关属性, 其中的具体设置和动手写一个快速集成网易新闻,腾讯视频,头条首页的ScrollPageView,显示滚动视图这里面介绍的使用相同, 这里就不在赘述了

    属性部分.png

    注意设置这些控件的frame的时候使用到了一些常量, 请根据您项目的具体需要改变

    常量.png
    1. 设置部分
        override func viewDidLoad() {
            super.viewDidLoad()
            
            title = "简书个人主页"
            
            // 这个是必要的设置, 如果没有设置导致显示内容不正常, 请尝试设置这个属性
            automaticallyAdjustsScrollViewInsets = false
            
            
            //1. 添加子控制器为PageTableViewController或者继承自他的Controller,
            //   或者你可以参考PageTableViewController他里面的实现自行实现(可以使用UICollectionView)相关的代理和属性 并且设置delegate为self
            
            addChildVcs()
            
            // 2. 先添加contentView
            view.addSubview(contentView)
            // 3. 再添加headView
            view.addSubview(headView)
            // 4. 再添加topView
            view.addSubview(topView)
    
    // 添加子控制器的时候需要设置代理
        func addChildVcs() {
            let vc1 = PageTableViewController()
            
            vc1.delegate = self
            addChildViewController(vc1)
            
            let vc2 = Test1Controller()
            vc2.delegate = self
            addChildViewController(vc2)
    }
    
    
    1. 子控制器的代理方法实现部分
    // MARK:- PageTableViewDelegate - 监控子控制器中的tableview的滚动和更新相关的UI
    extension Vc8Controller: PageTableViewDelegate {
        
        // 设置将要显示的tableview的contentOffset.y
        func setupTableViewOffSetYWhenViewWillAppear(scrollView: UIScrollView) {
            
            // 当子控制器的tableview向上滚动很多的时候, 重新设置offY
            if offSetY > -(naviBarHeight + segmentViewHeight) {
                offSetY = -(naviBarHeight + segmentViewHeight)
            }
            print("\\\\\\\\(offSetY)--------------")
            
            scrollView.contentOffset.y = offSetY
        }
        
        // 根据子控制器的scrolView的偏移量来调整UI
        func scrollViewIsScrolling(scrollView: UIScrollView) {
            let deltaY =  scrollView.contentOffset.y - offSetY
            offSetY = scrollView.contentOffset.y
            
            
    //        print(offSetY)
            
            if offSetY > -(defaultOffSetY - headViewHeight) {
                // 使滑块停在navigationBar下面
                headView.frame.origin.y = naviBarHeight - headViewHeight
                topView.frame.origin.y = naviBarHeight
                return
            } else if offSetY < -defaultOffSetY {
                
                // 使headView停在navigationBar下面
                headView.frame.origin.y = naviBarHeight
                topView.frame.origin.y = naviBarHeight + headViewHeight
                return
            }
            
            // 这里是让滑块和headView随着上下滚动
            headView.frame.origin.y -= deltaY
            topView.frame.origin.y -= deltaY
        }
        
    }
    
    
    
    1. contentView的代理方法实现
    // MARK:- ContentViewDelegate
    extension Vc8Controller: ContentViewDelegate {
        var segmentView: ScrollSegmentView {
            return topView
        }
        
    }
    
    1. 子控制器部分, 这里比较简单, 只需要实现scrollView的滚动代理方法, 在里面通过代理调用子控制器的代理方法, 通知给父控制器, 然后是在viewWillAppear()的方法中通过代理更新tableView(collectioVew)的contentOffset

    注意 /// !!! 不要在viewDidLoad()方法里面设置tableView或者collectionView的偏移量, 在初始化方法中设置偏移量,否则可能导致显示不正常

    // 协议方法

    // MARK: PageTableViewDelegate
    protocol PageTableViewDelegate: class {
        func scrollViewIsScrolling(scrollView: UIScrollView)
        func setupTableViewOffSetYWhenViewWillAppear(scrollView: UIScrollView)
    }
    
    
    

    初始化

      func setupTableView() {
            tableView = UITableView(frame: self.view.bounds, style: .Plain)
            tableView.delegate = self
            tableView.dataSource = self
            // 设置tableview的内容偏移量
    
            tableView.contentInset = UIEdgeInsets(top: defaultOffSetY, left: 0, bottom: 0, right: 0)
            
            self.view.addSubview(tableView)
        }
        
        
        /// !!! 不要在viewDidLoad()方法里面设置tableView或者collectionView的偏移量, 在初始化方法中设置偏移量,否则可能导致显示不正常
        
        init() {
            super.init(nibName: nil, bundle: nil)
            setupTableView()
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
        
    
    

    viewWillAppear

        override func viewWillAppear(animated: Bool) {
            super.viewWillAppear(true)
            // 通知父控制器重新设置tableView的contentOffset.y
            delegate?.setupTableViewOffSetYWhenViewWillAppear(tableView)
    //        print(tableView.contentOffset.y)
        }
    

    UIScrollViewDelegate

    // MARK: UIScrollViewDelegate - 监控tableview的滚动, 将改变通知给通知父控制器
    extension PageTableViewController: UIScrollViewDelegate {
        func scrollViewDidScroll(scrollView: UIScrollView) {
            delegate?.scrollViewIsScrolling(scrollView)
        }
    }
    
    
    

    源码地址,已更新, 如果您觉得有帮助,不妨给个star鼓励一下,欢迎关注, 欢迎交流


    相关文章

      网友评论

      • 王大大大大大大er:有没有人写好了这个OC版本的啊, 我的QQ980563520
      • 白与红与黑:写的很详细,是我见过这类效果里面写的最好的了,哇哦 大神 心很细哦
        ZeroJ:@梦想成为美人的丑女 :smiley: 夸奖了
      • angelen:OC版本没有简书主页效果?
        ZeroJ:@angelen 恩, 我明白你说的问题了, 这个是肯定会有的, 当你的数据很少的时候, tableView相当于是不能滚动或者滚动范围很少的, 所以可能出现向上滚动推不上去head或者向下的时候突然就下来了 , 你需要保证你的tableView的最小滚动的y为(屏幕高度+ head高度)
        angelen:@ZeroJ 我刚刚就是看了一下你那个 Swift 版本的,发现有一个之前我也遇到的 BUG:就是比如一个 Tableview 的数据比较少,比如只有 3 行这样子,滑动切换的时候就会有一种唐突的跳跃感(不懂那个现象具体怎么描述🙈)......我不知道怎么解决
        ZeroJ:@angelen oc版实现这个简书主页效果的话, 利用ZJScrollPageView很容易实现的, 我只写了swift示例, oc版的可以参照下实现了
      • 不会骑名字:大神,你这个好像有个bug,重现步骤简书个人主页使用tableview,让sem悬浮后把第一列数据向上滚动些较大偏移量,第三个也向上滚动些较大偏移量,在回到第二个,向下拉的时候会上sem和header消失,建议在加个变量记录当前出现时的tab的偏移量,如有不对望请见谅
        不会骑名字:@ZeroJ 不好意思 大神 我还有个疑惑 还是这个页面的 如果 我的header想接收用户响应 又想拖动的时候下面的top和tableview也一起向上或者向下滚动 是不是必须给header加个pan手势然后自己判断下?
        不会骑名字:@ZeroJ 好的,谢谢:blush:
        ZeroJ:@真的不会起名字啊 朋友, 这个bug之前确实存在, 不过不是因为少了个变量来记录当前时的tab的偏移量, 因为里面已经有一个变量实现了类似的功能, 我去看了看发现是因为不知道为何, 当使用了继承至Controller后super.viewWillAppear()有时可能不会被正确调用使得页面出现时的准备调整失败, 我调整了一下使用ScrollPageView发布的页面出现通知来调整页面, 目前发现效果还可以,你可以下载最新的代码看看
      • Qinz:不错,👍
        ZeroJ:@Qinz :smile: :smile:
      • 花前月下:喜欢你写的效果。不错,支持下
        ZeroJ:@花前月下 :smile: , 多谢支持, 不尽完美, 供参考
      • BUn_7:对照着写个0C版本的先
        ZeroJ:@BUn_7 朋友, oc版的这里有, 需要的话可以看看 https://github.com/jasnig/ZJScrollPageView
        ZeroJ:@BUn_7 :smile: :smile: ,好的好的
      • 8f8d573e1a36:感谢!
      • jswift:学习了
        jswift:@ZeroJ :smile:
        ZeroJ:@jswift :grin::grin:欢迎交流
        ZeroJ:@jswift :smile::smile:
      • SAW_:先mark了,有时间照着你的代码写个OC的
        SAW_:@ZeroJ :smile: 感谢分享
        ZeroJ:朋友, oc版的这里有, 需要的话可以看看 https://github.com/jasnig/ZJScrollPageView
        ZeroJ:@SAW :smile::smile:

      本文标题:实现微博个人页面的滑块浮动切换页面效果

      本文链接:https://www.haomeiwen.com/subject/yjntrttx.html