转载自:http://iyiming.me/blog/2015/07/05/custom-refresh-and-loading/

关于下拉刷新和上拉加载,项目中一直使用MJRefresh(原先还用过EGOTableViewPullRefresh,MJRefresh更好用些),今天就分析下如何用Swift来实现这个功能。

关于如何下拉刷新和上拉加载,认识到两点就可以了:

0.UIScrollViewDelegate

1.UIScrollView中的contentInset

直白一点说,就是在UIScrollView及其子类在下拉或者上拉到一定的偏移量时,设置contentInset,固定住UIScrollView,显示动画加载页面,数据到达之后再恢复contentInset。

UIScrollView很强大,除了上面说的,我们有时用它来显示放大缩小图片。

今天来自定义一个下拉刷新视图,并实现上拉加载功能。

前些天,一篇公众号(Pinapps,里面的文章挺好)文章里推荐了一个iPhone上的RSS阅读器Unread,软件做的很棒,就把全部功能都买了下来(程序内购买)。其中里面的下拉刷新效果挺棒的,正好仿一下:

分析下这里面的效果:

一开始线条是隐藏的,有了偏移后,线条从原先的窄线条慢慢变成宽线条线条,其中线条的颜色和REFRESH颜色也是有变化的。为了能做出连续性的动画我们使用POP(如果不是很了解POP的话,可以看一下我写的这篇文章)

已传至Github(时间仓促,没有加以优化):

https://github.com/iYiming/MyDemo/tree/master/Weibo

下面是代码的一些注释:

前提:添加POP动画,创建一个单视图项目,在ViewController里面添加一个UITableView。

先来实现刷新视图:即那三条红线加上REFRESH。

我们创建Swift文件,继承自UIView并命名为RefreshView:

代码如下:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  1. import UIKit
  2. @IBDesignable class RereshView: UIView {
  3. var topLayer: CALayer?//顶部线
  4. var middleLayer: CALayer?//中间线
  5. var bottomLayer: CALayer?//底部线
  6. var textLayer: CATextLayer?//REFRESH文字
  7. override init(frame: CGRect) {
  8. super.init(frame: frame)
  9. setup()//设置
  10. }
  11. required init(coder aDecoder: NSCoder) {
  12. super.init(coder: aDecoder)
  13. //fatalError("init(coder:) has not been implemented")
  14. setup()//设置
  15. }
  16. /**
  17. 设置
  18. */
  19. func setup(){
  20. //顶部线
  21. topLayer = CALayer()
  22. topLayer!.backgroundColor = UIColor(red: 200/255.0, green: 200/255.0, blue: 200/255.0, alpha: 1.0).CGColor
  23. topLayer!.frame = CGRectMake(98, 0, 4, 4)
  24. topLayer!.cornerRadius = 2
  25. self.layer.addSublayer(topLayer)
  26. //中间线
  27. middleLayer = CALayer()
  28. middleLayer!.backgroundColor = UIColor(red: 200/255.0, green: 200/255.0, blue: 200/255.0, alpha: 1.0).CGColor
  29. middleLayer!.frame = CGRectMake(98, 14, 4, 4)
  30. middleLayer!.cornerRadius = 2
  31. self.layer.addSublayer(middleLayer)
  32. //底部线
  33. bottomLayer = CALayer()
  34. bottomLayer!.backgroundColor = UIColor(red: 200/255.0, green: 200/255.0, blue: 200/255.0, alpha: 1.0).CGColor
  35. bottomLayer!.frame = CGRectMake(98, 28, 4, 4)
  36. bottomLayer!.cornerRadius = 2
  37. self.layer.addSublayer(bottomLayer)
  38. //REFRESH
  39. textLayer = CATextLayer()
  40. textLayer!.foregroundColor = UIColor(red: 64/255.0, green: 64/255.0, blue: 64/255.0, alpha: 1.0).CGColor
  41. textLayer!.fontSize = 10
  42. textLayer!.contentsScale = UIScreen.mainScreen().scale
  43. textLayer!.string = "REFRESH"
  44. textLayer!.opacity = 0
  45. textLayer!.frame = CGRectMake(75, 62, 50, 20)
  46. self.layer.addSublayer(textLayer)
  47. }
  48. }

注意我们在Class前面加了一个@IBDesignable,这样我们就能在Storyboard中查看我们自定义的视图(如果想详细了解的话,请看下Onevcat的可视化开发,IB新时代)。

在ViewController.swift中我们添加了如下代码:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 115
  116. 116
  117. 117
  118. 118
  119. 119
  120. 120
  121. 121
  122. 122
  123. 123
  124. 124
  125. 125
  126. 126
  127. 127
  128. 128
  129. 129
  130. 130
  131. 131
  132. 132
  133. 133
  134. 134
  135. 135
  136. 136
  137. 137
  138. 138
  139. 139
  140. 140
  141. 141
  142. 142
  143. 143
  144. 144
  145. 145
  146. 146
  147. 147
  148. 148
  149. 149
  150. 150
  151. 151
  152. 152
  153. 153
  154. 154
  155. 155
  156. 156
  157. 157
  158. 158
  159. 159
  160. 160
  161. 161
  162. 162
  163. 163
  164. 164
  165. 165
  166. 166
  167. 167
  168. 168
  169. 169
  170. 170
  171. 171
  172. 172
  173. 173
  174. 174
  175. 175
  176. 176
  177. 177
  178. 178
  179. 179
  180. 180
  181. 181
  182. 182
  183. 183
  184. 184
  185. 185
  186. 186
  187. 187
  188. 188
  189. 189
  190. 190
  191. 191
  192. 192
  193. 193
  194. 194
  195. 195
  196. 196
  197. 197
  198. 198
  199. 199
  200. 200
  201. 201
  202. 202
  203. 203
  204. 204
  205. 205
  206. 206
  207. 207
  208. 208
  209. 209
  210. 210
  211. 211
  212. 212
  213. 213
  214. 214
  215. 215
  216. 216
  217. 217
  218. 218
  219. 219
  220. 220
  221. 221
  222. 222
  223. 223
  224. 224
  225. 225
  226. 226
  227. 227
  228. 228
  229. 229
  230. 230
  231. 231
  232. 232
  233. 233
  234. 234
  235. 235
  236. 236
  1. import UIKit
  2. class ViewController: UIViewController,UITableViewDataSource,UIScrollViewDelegate {
  3. @IBOutlet weak var tableView: UITableView!//列表视图
  4. @IBOutlet weak var refreshView: RereshView!//下拉刷新视图
  5. @IBOutlet weak var refreshViewTopLayoutConstraint: NSLayoutConstraint!//下拉刷新居上的约束
  6. var showBottomLayer:Bool = true//显示刷新视图的底部线 变宽
  7. var hiddenBottomLayer:Bool = true//隐藏刷新视图的底部线 即变窄
  8. var showMiddleLayer:Bool = true//显示刷新视图的中间线 变宽
  9. var hiddenMiddleLayer:Bool = true//隐藏刷新视图的中间线 变窄
  10. var showTopLayer:Bool = true//显示刷新头部的底部线 变宽
  11. var hiddenTopLayer:Bool = true//隐藏刷新视图的头部线 变窄
  12. var refreshing = false//是否在刷新中
  13. var loadingAcitivityIndicatorView: UIActivityIndicatorView?//上拉加载指示菊花
  14. var showFooterLoadingView:Bool = false//是否显示上拉加载视图
  15. override func viewDidLoad() {
  16. super.viewDidLoad()
  17. // Do any additional setup after loading the view, typically from a nib.
  18. settingUI()//设置界面
  19. }
  20. override func didReceiveMemoryWarning() {
  21. super.didReceiveMemoryWarning()
  22. // Dispose of any resources that can be recreated.
  23. }
  24. //设置界面
  25. func settingUI(){
  26. let screenWidth = CGRectGetWidth(UIScreen.mainScreen().bounds)
  27. let screenHeight = CGRectGetHeight(UIScreen.mainScreen().bounds)
  28. loadingAcitivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
  29. loadingAcitivityIndicatorView?.hidesWhenStopped = true
  30. loadingAcitivityIndicatorView?.frame = CGRectMake((screenWidth - 20)/2.0,screenHeight - 40, 20, 20)
  31. self.view.addSubview(loadingAcitivityIndicatorView!)
  32. }
  33. /**
  34. 添加size动画
  35. :param: layer 要添加动画的layer
  36. :param: size 动画到的size
  37. */
  38. func addSizeAnimation(layer: CALayer,size: CGSize){
  39. var springAnimation = POPSpringAnimation(propertyNamed: kPOPLayerSize)
  40. springAnimation.toValue = NSValue(CGSize: size);
  41. springAnimation.springBounciness = 18
  42. layer.pop_addAnimation(springAnimation, forKey: "layerSpringAnimation")
  43. }
  44. /**
  45. 添加position动画
  46. :param: layer 要添加动画的layer
  47. :param: position 动画到的position
  48. */
  49. func addPositionAnimation(layer: CALayer,position: CGPoint){
  50. var springAnimation = POPSpringAnimation(propertyNamed: kPOPLayerPosition)
  51. springAnimation.toValue = NSValue(CGPoint: position);
  52. springAnimation.springBounciness = 18
  53. layer.pop_addAnimation(springAnimation, forKey: "layerSpringAnimation")
  54. }
  55. //UITableViewDataSource
  56. func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  57. return 100
  58. }
  59. func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  60. var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
  61. cell.textLabel?.text = "\(indexPath.row)"
  62. return cell
  63. }
  64. //UIScrollViewDelegate
  65. //将要开始减速时
  66. func scrollViewWillBeginDecelerating(scrollView: UIScrollView) {
  67. var contentOffsetY = scrollView.contentOffset.y
  68. if contentOffsetY > 0{
  69. return
  70. }
  71. if contentOffsetY < -52{//偏移 52 时 我们显示加载视图
  72. refreshing = true
  73. scrollView.contentInset = UIEdgeInsetsMake(62.0, 0.0, 0.0, 0.0)
  74. refreshViewTopLayoutConstraint.constant = 20//刷新视图居上约束
  75. //移除POP动画
  76. refreshView.topLayer?.pop_removeAllAnimations()
  77. refreshView.middleLayer?.pop_removeAllAnimations()
  78. refreshView.bottomLayer?.pop_removeAllAnimations()
  79. //设定layer位置
  80. refreshView.topLayer?.frame = CGRectMake(121, 0, 4, 4)
  81. refreshView.middleLayer?.frame = CGRectMake(98, 14, 4, 4)
  82. refreshView.bottomLayer?.frame = CGRectMake(75, 28, 4, 4)
  83. refreshView.textLayer?.opacity = 0
  84. self.addPositionAnimation(self.refreshView.topLayer!,position: CGPointMake(121, 14))
  85. self.addPositionAnimation(self.refreshView.middleLayer!,position: CGPointMake(98, 14))
  86. self.addPositionAnimation(self.refreshView.bottomLayer!,position: CGPointMake(75, 14))
  87. let delayTime = dispatch_time(DISPATCH_TIME_NOW,Int64(1 * Double(NSEC_PER_SEC)))
  88. dispatch_after(delayTime,dispatch_get_main_queue()) {
  89. UIView.animateWithDuration(0.3, animations: { () -> Void in
  90. scrollView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
  91. self.refreshViewTopLayoutConstraint.constant = -52
  92. }, completion: { (Bool) -> Void in
  93. self.refreshing = false
  94. self.refreshView.topLayer!.frame = CGRectMake(98, 0, 4, 4)
  95. self.refreshView.middleLayer!.frame = CGRectMake(98, 14, 4, 4)
  96. self.refreshView.bottomLayer!.frame = CGRectMake(98, 28, 4, 4)
  97. })
  98. }
  99. }
  100. }
  101. //滚动时
  102. func scrollViewDidScroll(scrollView: UIScrollView) {
  103. var contentOffsetY = scrollView.contentOffset.y
  104. if contentOffsetY <= 0 {
  105. if refreshing{
  106. return;
  107. }
  108. self.refreshView.hidden = false
  109. refreshViewTopLayoutConstraint.constant = -contentOffsetY - 52
  110. if contentOffsetY < -30{
  111. self.hiddenBottomLayer = true
  112. if showBottomLayer{
  113. showBottomLayer = false
  114. self.addSizeAnimation(refreshView.bottomLayer!, size: CGSizeMake(50, 4));
  115. refreshView.bottomLayer?.backgroundColor = UIColor(red: 64/255.0, green: 64/255.0, blue: 64/255.0, alpha: 1.0).CGColor
  116. }
  117. if contentOffsetY < -46{
  118. self.hiddenMiddleLayer = true
  119. if showMiddleLayer{
  120. showMiddleLayer = false
  121. self.addSizeAnimation(refreshView.middleLayer!, size: CGSizeMake(50, 4));
  122. refreshView.middleLayer?.backgroundColor = UIColor(red: 64/255.0, green: 64/255.0, blue: 64/255.0, alpha: 1.0).CGColor
  123. }
  124. if contentOffsetY < -62{
  125. self.hiddenTopLayer = true
  126. if showTopLayer{
  127. showTopLayer = false
  128. self.addSizeAnimation(refreshView.topLayer!, size: CGSizeMake(50, 4));
  129. refreshView.topLayer?.backgroundColor = UIColor(red: 192/255.0, green: 47/255.0, blue: 46/255.0, alpha: 1.0).CGColor
  130. refreshView.middleLayer?.backgroundColor = UIColor(red: 192/255.0, green: 47/255.0, blue: 46/255.0, alpha: 1.0).CGColor
  131. refreshView.bottomLayer?.backgroundColor = UIColor(red: 192/255.0, green: 47/255.0, blue: 46/255.0, alpha: 1.0).CGColor
  132. refreshView.textLayer?.foregroundColor = UIColor(red: 192/255.0, green: 47/255.0, blue: 46/255.0, alpha: 1.0).CGColor
  133. }
  134. }else{
  135. self.showTopLayer = true
  136. if hiddenTopLayer{
  137. hiddenTopLayer = false
  138. self.addSizeAnimation(refreshView.topLayer!, size: CGSizeMake(4, 4));
  139. refreshView.topLayer?.backgroundColor = UIColor(red: 200/255.0, green: 200/255.0, blue: 200/255.0, alpha: 1.0).CGColor
  140. refreshView.middleLayer?.backgroundColor = UIColor(red: 64/255.0, green: 64/255.0, blue: 64/255.0, alpha: 1.0).CGColor
  141. refreshView.bottomLayer?.backgroundColor = UIColor(red: 64/255.0, green: 64/255.0, blue: 64/255.0, alpha: 1.0).CGColor
  142. refreshView.textLayer?.foregroundColor = UIColor(red: 64/255.0, green: 64/255.0, blue: 64/255.0, alpha: 1.0).CGColor
  143. }
  144. }
  145. }else{
  146. self.showMiddleLayer = true
  147. if hiddenMiddleLayer{
  148. hiddenMiddleLayer = false
  149. self.addSizeAnimation(refreshView.middleLayer!, size: CGSizeMake(4, 4));
  150. refreshView.middleLayer?.backgroundColor = UIColor(red: 200/255.0, green: 200/255.0, blue: 200/255.0, alpha: 1.0).CGColor
  151. }
  152. }
  153. }else{
  154. self.showBottomLayer = true
  155. if hiddenBottomLayer{
  156. hiddenBottomLayer = false
  157. self.addSizeAnimation(refreshView.bottomLayer!, size: CGSizeMake(4, 4));
  158. refreshView.bottomLayer?.backgroundColor = UIColor(red: 200/255.0, green: 200/255.0, blue: 200/255.0, alpha: 1.0).CGColor
  159. }
  160. }
  161. refreshView.textLayer?.opacity = Float(-contentOffsetY/62.0)
  162. }else{
  163. self.refreshView.hidden = true
  164. let screenWidth = CGRectGetWidth(UIScreen.mainScreen().bounds)
  165. }
  166. if scrollView.contentOffset.y + scrollView.frame.size.height < scrollView.contentSize.height{
  167. loadingAcitivityIndicatorView?.stopAnimating()
  168. tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
  169. showFooterLoadingView = false
  170. }else{
  171. tableView.contentInset = UIEdgeInsetsMake(0, 0, 60, 0);
  172. loadingAcitivityIndicatorView?.startAnimating()
  173. showFooterLoadingView = true;
  174. //添加上拉加载数据代码
  175. //...
  176. }
  177. }
  178. }

需要注明的是:

其中下拉刷新视图和上拉加载视图的父级视图是ViewController.view。

下拉刷新和上拉加载 Swift的更多相关文章

  1. android--------自定义控件ListView实现下拉刷新和上拉加载

    开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,为了方便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能. Android下拉刷新可以分为两种情况: 1.获取 ...

  2. Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

    RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...

  3. iscroll.js 下拉刷新和上拉加载

    html代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  4. IOS 开发下拉刷新和上拉加载更多

    IOS 开发下拉刷新和上拉加载更多 简介 1.常用的下拉刷新的实现方式 (1)UIRefreshControl (2)EGOTTableViewrefresh (3)AH3DPullRefresh ( ...

  5. IOS UITableView下拉刷新和上拉加载功能的实现

    在IOS开发中UITableView是非常常用的一个功能,而在使用UITableView的时候我们经常要用到下拉刷新和上拉加载的功能,今天花时间实现了简单的UITableView的下拉刷新和上拉加载功 ...

  6. Android 使用PullToRefresh实现下拉刷新和上拉加载(ExpandableListView)

    PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的Vie ...

  7. 使用PullToRefresh实现下拉刷新和上拉加载

    使用PullToRefresh实现下拉刷新和上拉加载 分类: Android2013-12-20 15:51 78158人阅读 评论(91) 收藏 举报 Android下拉刷新上拉加载PullToRe ...

  8. H5下拉刷新和上拉加载实现原理浅析

    前言 在移动端H5网页中,下拉刷新和上拉加载更多数据的交互方式出现频率很高,开源社区也有很多类似的解决方案,如iscroll,pulltorefresh.js库等.下面是对这两种常见交互基本实现原理的 ...

  9. 安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)

    前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添 ...

随机推荐

  1. Multidimensional Arrays

    Overview An array having more than two dimensions is called a multidimensional array in the MATLAB® ...

  2. C的指针,真的很经典

    工作以后,一直使用C++,也做过Objective C,各种类的方法封装得很好,使用很简单,今天偶尔翻看一下 严蔚敏 的 <数据结构>,第一个程序demo就看了半天,一是由于demo的变量 ...

  3. 洛谷-A+B Problem-洛谷的第一个任务

    题目描述 Description 输入两个整数a,b,输出它们的和(a,b<=10^9)  输入输出格式 Input/output 输入格式:两个整数以空格分开输出格式:一个数  输入输出样例  ...

  4. Mac 命令行中进入带有空格的文件夹

    http://blog.sina.com.cn/s/blog_5e8392b10100jkvg.html 今天在公司用mac的时候,有个文件夹的名字有空格,怎么都进不去,在网上一查原来不能直接cd 文 ...

  5. 测试sql性能方法

    SET STATISTICS io ON         SET STATISTICS time ON         go          ---你要测试的sql语句          selec ...

  6. ValueStack背后的OGNL表达式

    原文地址:http://blog.csdn.net/li_tengfei/archive/2010/12/25/6098134.aspx 前言: Strut2的Action类通过属性可以获得所有相关的 ...

  7. ios文本框基本使用,以及所有代理方法的作用

    /* UITextField文本输入框 */ UITextField * textField = [[UITextField alloc]initWithFrame:CGRectMake(50, 50 ...

  8. 转 s3c2440硬件学习----内存管理单元MMU

    本篇基本是韦东山书上的 一.内存管理单元MMU介绍 内存管理单元简称MMU,它负责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限检查.MMU使得每个用户进程拥有自己独立的地址空间,并通过内存访 ...

  9. 《Java Mail》

    <Java Mail> 文/冯皓林 完稿:2016.3.16--2016.3.19 “特定环境.一类问题.N个解决方案” 一.RFC821文档说明 核心: 邮件(Mail): 1.邮件头( ...

  10. 如何将excel导入到数据库中并在gridview中显示

    在页面上导入个excel文件,将该excel中的数据导入到数据库中,并且在页面的gridview中把数据显示出来. .在Asp.net中怎样将Excel文件中的数据导入到GridView中呢? 首先我 ...