从打造新闻app学到的(7):下拉刷新和上拉更多

新闻的实时性,必然导致其列表清单对“下拉刷新”和“上拉更多”的实现要求。一开始我以为这两个可以很容易地通过手势的处理来实现,找了找先是发现了个控件UIRefreshControl,很简单地实现了下拉刷新,想着上拉不是一样嘛,先用用再研究上拉怎么搞,于是放进了Code里面:

@IBOutlet weak var loading: UIActivityIndicatorView!
override func viewDidLoad() {
    //添加下拉刷新
    sliding.addTarget(self, action: "updateLatesList", forControlEvents: UIControlEvents.ValueChanged)
    sliding.attributedTitle = NSAttributedString(string: "下拉松手刷新...")
    self.ListTableView.addSubview(sliding)
}

func updateLatesList(){
    //TODO:刷新数据
    self.sliding.endRefreshing()
}

这个一看便知,注册一下回调,修改一下显示的字符串;然后在控件的监听回调里面刷新好数据,关掉动画,搞定!简直顺利得不敢相信。然后开始研究怎么通过这个改上拉更多……恕我驽钝,搞不定。上网查了一圈,貌似大家的上拉都不是用这个控件,这个控件的可定制性据说也比较差……😰没办法,乖乖自己写了。
去找了个第三方的“上拉更多”库,看源码(Sorry,已经不知道是哪个库了,GitHub上一大堆,我直接在浏览器里面看完就关了),发现里面的关键就是监听滑动事件,然后通过计算坐标来判断是否是上拉:

你看到的是非授权版本!爬虫凶猛,请尊重知识产权!

转载请注明出处:http://conanwhf.github.io/2016/01/01/newsreader-7/

访问原文「从打造新闻app学到的(7):下拉刷新和上拉更多」获取最佳阅读体验并参与讨论

func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool){  
if (scrollView.contentOffset.y > (scrollView.contentSize.height - scrollView.frame.size.height) + 70)  {//触发上拉刷新,70是阀值
// TODO anything
}

这里面有几个参数:

  • scrollView.contentOffset.y,这是指松开的时候显示的顶部处于ScrollView(在我的例子中是TableView)的什么位置
  • scrollView.contentSize.height,这是整个ScrollView的高度,你可以想象一下,整个View很长,通过滑动来显示某一部分,而这个contentSize就是整个的Size
  • scrollView.frame.size.height,这是ScrollView显示出来的部分的高度
    所以这个判断条件就很好理解了。如下图所示:

    当滑动条拉到最下的时候,contentOffset.y = contentSize.height-frame.size.height,而继续上拉,就会有一段空白,定义这个空白的高度(我设的70),就是上拉刷新的阈值了。同样的,用这个办法也可以判断下拉刷新,而且更加简单:contentOffset.y < -70
    判断问题解决了,剩下的就是UI的事儿了。我选择了最基本的UIActivityIndicatorView,转菊花,简单到只要在对应的时间start&Stop Animating就行了。于是现在把上拉更多和下拉刷新结合一下,用同一套方法对付:

    private let loading = UIActivityIndicatorView()
    override func viewDidLoad() {
        self.ListTableView.addSubview(loading)
        loading.hidesWhenStopped = true
    }
    
    func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    
    if (scrollView.contentOffset.y > (scrollView.contentSize.height - scrollView.frame.size.height) + 70) && (tableViewData.count > 0) {//触发上拉刷新
        loading.startAnimating()
        //TODO: 要求更多数据并显示
        loading.stopAnimating()
    }
    if (scrollView.contentOffset.y < -70  && manager.wxcList.count > 0) {//触发下拉刷新
        loading.startAnimating()
        //TODO: 要求新数据并显示
        loading.stopAnimating()
    }
    }
    

    是不是核心代码少得让人觉得自己没写code?😄在上拉和下拉的判断中我加了一个条件就是TableView的数据源不为空,不然进行到数据刷新的时候会出错,这个也算是相对比较有普适性,所以放上来提一下。另外有几细节需要注意一下:

  1. UIActivityIndicatorView作为TableView的子View的时候,默认是加到Cell最后的,所以如果什么都不配置,下拉刷新是看不到转圈圈的。具体怎么配置、能不能配置其实我还没去看;
  2. 如果要把菊花加到主视图上,也是需要配置才能看得见,包括大小、位置、层级等等
  3. 所有跟UI相关的刷新操作必须放在主进程中,否则可能无效。多线程是另外一块东西了,等过些天我整理好再写

目前我还是用“UIRefreshControl做下拉+ UIActivityIndicatorView做上拉”的组合,毕竟原生下拉控件安全方便又好看😊。但这个、_scrollViewDidEndDragging_的监听是个很好的例子,通过它我想我们还能做更多包括侧滑、翻页等等等等,当然真做起来还是需要结合别的Event。