GTMRefresh 用Swift重写的MJRefresh
- 自定义方便, Demo里面有国内主流App的下拉效果的模仿
- 代码简洁,总代码量不超过1000行
- 支持国际化
- 支持: UITableView, UICollectionView, UIScrollView, UIWebView
直接下载代码,里面Demo里面有各种效果的自定义效果(因为时间比较紧,demo代码可能不够漂亮)
alt tag alt tag alt tag alt tag alt tag
- YahooWeather
- Curve Mask
- Youku
- TaoBao
- QQ Video
- DianPing
Install Cocoapods if need be.
$ gem install cocoapods
Add GTMRefresh in your Podfile.
use_frameworks! pod 'GTMRefresh'
Then, run the following command.
$ pod install
Copy GTMRefresh folder to your project. That's it.
Note: Make sure that all files in GTMRefresh included in Compile Sources in Build Phases.
This version requires Xcode 11.0 and Swift 5.
Firstly, import GTMRefresh.
import GTMRefreshself.tableView.gtm_addRefreshHeaderView { [weak self] in print("excute refreshBlock") self.refresh() } self.tableView.gtm_addLoadMoreFooterView { [weak self] in print("excute loadMoreBlock") self.loadMore() }
// text self.tableView.pullDownToRefreshText("亲,试试下拉会刷新的") .releaseToRefreshText("亲,松开试试") .refreshSuccessText("亲,成功了") .refreshFailureText("亲,无果") .refreshingText("亲,正在努力刷新") // color self.tableView.headerTextColor(.red) // icon self.tableView.headerIdleImage(UIImage.init(named: "dropdown_anim__00048"))
self.tableView.triggerRefreshing()
约定
- 必须继承 GTMRefreshHeader
- 必须实现 SubGTMRefreshHeaderProtocol
SubGTMRefreshHeaderProtocol
public protocol SubGTMRefreshHeaderProtocol { /// 状态变成.idle func toNormalState() /// 状态变成.refreshing func toRefreshingState() /// 状态变成.pulling func toPullingState() /// 状态变成.willRefresh func toWillRefreshState() /// 下拉高度/触发高度 值改变 func changePullingPercent(percent: CGFloat) /// 开始结束动画前执行 func willBeginEndRefershing(isSuccess: Bool) /// 结束动画完成后执行 func willCompleteEndRefershing() /// 控件的高度 /// /// - Returns: 控件的高度 func contentHeight() -> CGFloat }
- 当触发刷新的高度和控件高度不一样时重写willRefresHeight(),如Demo里的:Curve Mask
/// 即将触发刷新的高度(特殊的控件需要重写该方法,返回不同的数值) /// /// - Returns: 触发刷新的高度 open func willRefresHeight() -> CGFloat { return self.mj_h // 默认使用控件高度 }
- 当Loadding动画显示区域的高度和控件高度不一样时重写refreshingHoldHeight(),如Demo里的:QQ
/// Loadding动画显示区域的高度(特殊的控件需要重写该方法,返回不同的数值) /// /// - Returns: Loadding动画显示区域的高度 open func refreshingHoldHeight() -> CGFloat { return self.mj_h // 默认使用控件高度 }
- 当 footer 触发加载更多的高度和控件高度不一样时重写willLoadMoreHeight()
/// 即将触发加载的高度(特殊的控件需要重写该方法,返回不同的数值) /// /// - Returns: 触发刷新的高度 open func willLoadMoreHeight() -> CGFloat { return self.mj_h // 默认使用控件高度 } }
// // TaoBaoRefreshHeader.swift // PullToRefreshKit // // Created by luoyang on 2016年12月8日. // Copyright © 2016年 luoyang. All rights reserved. // import UIKit import GTMRefresh class TaoBaoRefreshHeader: GTMRefreshHeader, SubGTMRefreshHeaderProtocol { fileprivate let circleLayer = CAShapeLayer() fileprivate let arrowLayer = CAShapeLayer() let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 230, height: 35)) fileprivate let textLabel = UILabel() fileprivate 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.textAlignment = .center textLabel.textColor = UIColor.lightGray textLabel.font = UIFont.systemFont(ofSize: 14) textLabel.text = "下拉即可刷新..." imageView.image = UIImage(named: "taobaoLogo") self.contentView.addSubview(imageView) self.contentView.addSubview(textLabel) } func setUpArrowLayer(){ let bezierPath = UIBezierPath() bezierPath.move(to: CGPoint(x: 20, y: 15)) bezierPath.addLine(to: CGPoint(x: 20, y: 25)) bezierPath.addLine(to: CGPoint(x: 25,y: 20)) bezierPath.move(to: CGPoint(x: 20, y: 25)) bezierPath.addLine(to: CGPoint(x: 15, y: 20)) self.arrowLayer.path = bezierPath.cgPath self.arrowLayer.strokeColor = UIColor.lightGray.cgColor self.arrowLayer.fillColor = UIColor.clear.cgColor self.arrowLayer.lineWidth = 1.0 self.arrowLayer.lineCap = kCALineCapRound self.arrowLayer.bounds = CGRect(x: 0, y: 0,width: 40, height: 40) self.arrowLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5) self.layer.addSublayer(self.arrowLayer) } func setUpCircleLayer(){ let bezierPath = UIBezierPath(arcCenter: CGPoint(x: 20, y: 20), radius: 12.0, startAngle:CGFloat(-Double.pi/2), endAngle: CGFloat(Double.pi * 1.5), clockwise: true) self.circleLayer.path = bezierPath.cgPath self.circleLayer.strokeColor = UIColor.lightGray.cgColor self.circleLayer.fillColor = UIColor.clear.cgColor self.circleLayer.strokeStart = 0.05 self.circleLayer.strokeEnd = 0.05 self.circleLayer.lineWidth = 1.0 self.circleLayer.lineCap = kCALineCapRound self.circleLayer.bounds = CGRect(x: 0, y: 0,width: 40, height: 40) self.circleLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5) self.layer.addSublayer(self.circleLayer) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() textLabel.frame = CGRect(x: 0,y: 0,width: 120, height: 40) //放置Views和Layer imageView.center = CGPoint(x: frame.width/2, y: frame.height - 60 - 18) textLabel.center = CGPoint(x: frame.width/2 + 20, y: frame.height - 30) self.arrowLayer.position = CGPoint(x: frame.width/2 - 60, y: frame.height - 30) self.circleLayer.position = CGPoint(x: frame.width/2 - 60, y: frame.height - 30) } func toNormalState() {} func toRefreshingState() { self.circleLayer.strokeEnd = 0.95 let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z") rotateAnimation.toValue = NSNumber(value: Double.pi * 2.0 as Double) rotateAnimation.duration = 0.6 rotateAnimation.isCumulative = true rotateAnimation.repeatCount = 10000000 self.circleLayer.add(rotateAnimation, forKey: "rotate") self.arrowLayer.isHidden = true textLabel.text = "刷新中..." } func toPullingState() {} func toWillRefreshState() {} func changePullingPercent(percent: CGFloat) { let adjustPercent = max(min(1.0, percent),0.0) if adjustPercent == 1.0{ textLabel.text = "释放即可刷新..." }else{ textLabel.text = "下拉即可刷新..." } self.circleLayer.strokeEnd = 0.05 + 0.9 * adjustPercent } func willBeginEndRefershing(isSuccess: Bool) {} func willCompleteEndRefershing() { transitionWithOutAnimation { self.circleLayer.strokeEnd = 0.05 }; self.circleLayer.removeAllAnimations() self.arrowLayer.isHidden = false textLabel.text = "下拉即可刷新" } func contentHeight()->CGFloat{ return 60 } /// MARK: Private func transitionWithOutAnimation(_ clousre:()->()){ CATransaction.begin() CATransaction.setDisableActions(true) clousre() CATransaction.commit() } }
self.tableView.gtm_addRefreshHeaderView(refreshHeader: CustomRefreshHeader()) { [weak self] in print("excute refreshBlock") self.refresh() }
约定
- 必须继承 GTMLoadMoreFooter
- 必须实现 SubGTMLoadMoreFooterProtocol
SubGTMLoadMoreFooterProtocol
public protocol SubGTMLoadMoreFooterProtocol { func toNormalState() func toNoMoreDataState() func toWillRefreshState() func toRefreshingState() /// 控件的高度(自定义控件通过该方法设置自定义高度) /// /// - Returns: 控件的高度 func contentHeith() -> CGFloat }
#参与开源 欢迎提交 issue 和 PR,大门永远向所有人敞开。
#开源协议 本项目遵循 MIT 协议开源,具体请查看根目录下的 LICENSE 文件。