用Swift實現淘寶和大眾點評的下拉重新整理
阿新 • • 發佈:2019-01-23
來自Leo的原創部落格,轉載請著名出處
我的StackOverflow
效果
淘寶
大眾點評
專案地址
其中
- 大眾點評的下拉重新整理用了50行左右程式碼
- 淘寶的下拉重新整理用了90行左右程式碼
Tips:用Swift 2.2寫的,所以需要XCode 7.3來執行。
PullToRefreshKit
這是我用純Swift 2.2寫的一個庫,初衷是為了更簡單的實現自定義下拉重新整理。當然,它也支援一程式碼實現:上拉載入,左/右滑動載入更多的操作。
比如,用一行程式碼實現預設的下拉重新整理
self.tableView.setUpHeaderRefresh { [weak self] in
delay(1.5, closure: {
self?.tableView.endHeaderRefreshing(.Success)
})
}
效果
不過,這個庫的主要目的還是希望大家能方便的實現自定義重新整理介面,不管是哪個方向的。
通過它來自定義重新整理介面,只需要實現三個協議中的一個。
比如,實現自定義下拉重新整理,只需寫一個UIView的子類,要遵循協議RefreshableHeader。這個UIView的子類,你可以用AutoLayout,任何你想要使用的佈局效果。
這個協議有如下幾個方法
//拖拽的觸發重新整理的距離,也是Header的高度
func distanceToRefresh()->CGFloat
//拖拽或者釋放的時候,比例的變化。比如總高度是100,當拖拽為10的時候,比例就是0.1,在這裡,可以根據百分比動態的設定Header的狀態
func percentUpdateWhenNotRefreshing(percent:CGFloat)
//鬆手即將進入重新整理狀態的回撥,在這裡,把檢視切換為動畫狀態
func releaseWithRefreshingState()
//重新整理結束,將要開始隱藏Header的動畫,在這裡告訴使用者重新整理失敗或者成功
func didBeginEndRefershingAnimation(result:RefreshResult)
//重新整理結束,Header完全隱藏的回撥,這裡把Header恢復到最初的狀態
func didCompleteEndRefershingAnimation(result:RefreshResult)
大眾點評下拉重新整理
分析一下,大眾點評的下拉重新整理主要分為兩個狀態
- 下拉的過程中,根據下拉程度,動態調整顯示的圖片
- 重新整理的時候,顯示動圖
首先,我們準備好圖片,本文的圖片來自於MJ哥的MJRefresh
其中,下拉的過程的圖片,一共有60張,重新整理的時候,動圖有3張。
這60張圖是不一樣的,比如第一張,第1,30,60張如下,下拉的過程就是不斷的切換圖片
至於重新整理的過程中,就是用Imageview,動態播放以下三張圖片罷了
所以,程式碼如下
class DianpingRefreshHeader:UIView,RefreshableHeader{
let imageView = UIImageView()
//放置Imageview
override init(frame: CGRect) {
super.init(frame: frame)
imageView.frame = CGRectMake(0, 0, 60, 60)
imageView.center = CGPointMake(CGRectGetWidth(self.bounds)/2.0, CGRectGetHeight(self.bounds)/2.0 + 10)
addSubview(imageView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - RefreshableHeader -
//一共的距離是70
func distanceToRefresh()->CGFloat{
return 60
}
//監聽百分比變化,切換圖片
func percentUpdateWhenNotRefreshing(percent:CGFloat){
imageView.hidden = (percent == 0)
let adjustPercent = max(min(1.0, percent),0.0)
let scale = 0.2 + (1.0 - 0.2) * adjustPercent;
imageView.transform = CGAffineTransformMakeScale(scale, scale)
let mappedIndex = Int(adjustPercent * 60)
let imageName = "dropdown_anim__000\(mappedIndex)"
let image = UIImage(named: imageName)
imageView.image = image
}
//鬆手即將重新整理,播放動圖
func releaseWithRefreshingState(){
let images = ["dropdown_loading_01","dropdown_loading_02","dropdown_loading_03"].map { (name) -> UIImage in
return UIImage(named:name)!
}
imageView.animationImages = images
imageView.animationDuration = 0.6
imageView.startAnimating()
}
//重新整理結束,將要隱藏header,不做任何處理
func didBeginEndRefershingAnimation(result:RefreshResult){
}
//重新整理結束,完全隱藏header,恢復到最初狀態
func didCompleteEndRefershingAnimation(result:RefreshResult){
imageView.animationImages = nil
imageView.stopAnimating()
imageView.hidden = true
}
}
然後,這樣呼叫
let dianpingHeader = DianpingRefreshHeader(frame: CGRectMake(0,0,CGRectGetWidth(self.view.bounds),60))
self.tableView.setUpHeaderRefresh(taobaoHeader) { [weak self] in
delay(1.5, closure: {
self?.tableView.endHeaderRefreshing(.Success)
})
}
淘寶下拉重新整理
首先分析檢視架構
整個下拉重新整理的介面如下
其中
- 是CAShapeLayer,隨著滑動,動態調整繪製過程,重新整理的時候轉圈圈
- 是CAShapeLayer,靜態的,當重新整理的時候隱藏
- 簡單的UILabel
- 在重新整理介面上面還有一個Imageview,建立一個Imageview放置到上面即可
所以,完整的程式碼如下
class TaoBaoRefreshHeader:UIView,RefreshableHeader{
private let circleLayer = CAShapeLayer()
private let arrowLayer = CAShapeLayer()
private let textLabel = UILabel()
private let strokeColor = UIColor(red: 135.0/255.0, green: 136.0/255.0, blue: 137.0/255.0, alpha: 1.0)
override init(frame: CGRect) {
super.init(frame: frame)
setUpCircleLayer()
setUpArrowLayer()
textLabel.frame = CGRectMake(CGRectGetWidth(self.bounds)/2 - 30, CGRectGetHeight(self.bounds)/2 - 20,120, 40)
textLabel.textAlignment = .Center
textLabel.textColor = UIColor.lightGrayColor()
textLabel.font = UIFont.systemFontOfSize(14)
textLabel.text = "下拉即可重新整理..."
self.addSubview(textLabel)
let imageView = UIImageView(frame: CGRectMake(0, 0, 230, 35))
imageView.image = UIImage(named: "taobaoLogo")
self.addSubview(imageView)
self.addSubview(textLabel)
}
//繪製中間箭頭
func setUpArrowLayer(){
//略去
}
//繪製外部圓圈
func setUpCircleLayer(){
//略去
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - RefreshableHeader -
func distanceToRefresh()->CGFloat{
return 60
}
//根據滑動百分比,動態調整storkeEnd和文字
func percentUpdateWhenNotRefreshing(percent:CGFloat){
let adjustPercent = max(min(1.0, percent),0.0)
self.circleLayer.strokeEnd = 0.05 + (0.95 - 0.05) * adjustPercent
if adjustPercent == 1.0{
textLabel.text = "釋放即可重新整理..."
}else{
textLabel.text = "下拉即可重新整理..."
}
}
//進入重新整理狀態,調整圈圈的strokeEnd,為圈圈增加旋轉動畫,更新label文字
func releaseWithRefreshingState(){
self.circleLayer.strokeEnd = 0.95
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.toValue = NSNumber(double: M_PI * 2.0)
rotateAnimation.duration = 0.6
rotateAnimation.cumulative = true
rotateAnimation.repeatCount = 10000000
self.circleLayer.addAnimation(rotateAnimation, forKey: "rotate")
self.arrowLayer.hidden = true
textLabel.text = "重新整理中..."
}
//結束重新整理的動畫開始,停止動畫
func didBeginEndRefershingAnimation(result:RefreshResult){
self.circleLayer.removeAllAnimations()
}
//Header完全隱藏,恢復到原始狀態
func didCompleteEndRefershingAnimation(result:RefreshResult){
self.circleLayer.strokeEnd = 0.05
self.arrowLayer.hidden = false
textLabel.text = "下拉即可重新整理"
然後這樣使用
let taobaoHeader = TaoBaoRefreshHeader(frame: CGRectMake(0,0,CGRectGetWidth(self.view.bounds),100))
self.tableView.setUpHeaderRefresh(taobaoHeader) { [weak self] in
delay(1.5, closure: {
self?.models = (self?.models.map({_ in random100()}))! self?.tableView.endHeaderRefreshing(.Success)
})
}
Tips:注意到,TaoBaoRefreshHeader的frame的高度是100,但是distanceToRefresh()卻返回60,從而實現了頂部的ImageViwe在重新整理的時候隱藏
最後
如果你喜歡這個庫,歡迎和我一起把它完善,PullToRefreshKit ,歡迎各種建議,Star,fork。