转载

从打造新闻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() } 

这个一看便知,注册一下回调,修改一下显示的字符串;然后在控件的监听回调里面刷新好数据,关掉动画,搞定!简直顺利得不敢相信。然后开始研究怎么通过这个改上拉更多……恕我驽钝,搞不定。上网查了一圈,貌似大家的上拉都不是用这个控件,这个控件的可定制性据说也比较差……:cold_sweat:没办法,乖乖自己写了。

去找了个第三方的“上拉更多”库,看源码(Sorry,已经不知道是哪个库了,GitHub上一大堆,我直接在浏览器里面看完就关了),发现里面的关键就是监听滑动事件,然后通过计算坐标来判断是否是上拉:

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显示出来的部分的高度

    所以这个判断条件就很好理解了。如下图所示:

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

    当滑动条拉到最下的时候, 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?:smile:在上拉和下拉的判断中我加了一个条件就是TableView的数据源不为空,不然进行到数据刷新的时候会出错,这个也算是相对比较有普适性,所以放上来提一下。另外有几细节需要注意一下:

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

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

正文到此结束
Loading...