新闻的实时性,必然导致其列表清单对“下拉刷新”和“上拉更多”的实现要求。一开始我以为这两个可以很容易地通过手势的处理来实现,找了找先是发现了个控件 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显示出来的部分的高度
所以这个判断条件就很好理解了。如下图所示:
当滑动条拉到最下的时候,
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的数据源不为空,不然进行到数据刷新的时候会出错,这个也算是相对比较有普适性,所以放上来提一下。另外有几细节需要注意一下:
- UIActivityIndicatorView作为TableView的子View的时候,默认是加到Cell最后的,所以如果什么都不配置,下拉刷新是看不到转圈圈的。具体怎么配置、能不能配置其实我还没去看;
- 如果要把菊花加到主视图上,也是需要配置才能看得见,包括大小、位置、层级等等
- 所有跟UI相关的刷新操作必须放在主进程中,否则可能无效。 多线程是另外一块东西了,等过些天我整理好再写
目前我还是用“UIRefreshControl做下拉+ UIActivityIndicatorView做上拉”的组合,毕竟原生下拉控件安全方便又好看:blush:。但这个_scrollViewDidEndDragging_的监听是个很好的例子,通过它我想我们还能做更多包括侧滑、翻页等等等等,当然真做起来还是需要结合别的Event。