iOS-Swift-MJRefresh重寫

美景如畫

GTMRefresh github
===================
GTMRefresh 用Swift重寫的MJRefresh

Introduction

  • 自定義方便, Demo里面有國內(nèi)主流App的下拉效果的模仿
  • 代碼簡潔焊唬,總代碼量不超過1000行
  • 支持國際化
  • 支持: UITableView, UICollectionView, UIScrollView, UIWebView

Demo

直接下載代碼皆警,里面Demo里面有各種效果的自定義效果(因為時間比較緊,demo代碼可能不夠漂亮)

alt tag

alt tag
alt tag

alt tag
alt tag

alt tag

alt tag

Demo模仿的下拉效果

  • YahooWeather
  • Curve Mask
  • Youku
  • TaoBao
  • QQ Video
  • DianPing
  • QQ

Installation

Cocoapods

Install Cocoapods if need be.

$ gem install cocoapods

Add GTMRefresh in your Podfile.

use_frameworks!

pod 'GTMRefresh'

Then, run the following command.

$ pod install

Manual

Copy GTMRefresh folder to your project. That's it.

Note: Make sure that all files in GTMRefresh included in Compile Sources in Build Phases.

版本

Vesrion 0.0.1

This version requires Xcode 8.0 and Swift 3.

使用幫助

Firstly, import GTMRefresh.

import GTMRefresh

使用默認的下拉和上拉效果

    self.tableView.gtm_addRefreshHeaderView {
        [weak self] in
        print("excute refreshBlock")
        self.refresh()
    }

    self.tableView.gtm_addLoadMoreFooterView {
        [weak self] in
        print("excute loadMoreBlock")
        self.loadMore()
    }

代碼觸發(fā)刷新

    self.tableView.triggerRefreshing()

自定義下拉刷新效果

約定

  • 必須繼承 GTMRefreshHeader
  • 必須實現(xiàn) SubGTMRefreshHeaderProtocol

SubGTMRefreshHeaderProtocol

public protocol SubGTMRefreshHeaderProtocol {
    /// 狀態(tài)變成.idle
    func toNormalState()
    /// 狀態(tài)變成.refreshing
    func toRefreshingState()
    /// 狀態(tài)變成.pulling
    func toPullingState()
    /// 狀態(tài)變成.willRefresh
    func toWillRefreshState()
    /// 下拉高度/觸發(fā)高度 值改變
    func changePullingPercent(percent: CGFloat)
    /// 開始結束動畫前執(zhí)行
    func willBeginEndRefershing(isSuccess: Bool)
    /// 結束動畫完成后執(zhí)行
    func willCompleteEndRefershing()

    /// 控件的高度
    ///
    /// - Returns: 控件的高度
    func contentHeight() -> CGFloat
}

特殊效果的實現(xiàn)

  • 當觸發(fā)刷新的高度和控件高度不一樣時重寫willRefresHeight(),如Demo里的:Curve Mask
    /// 即將觸發(fā)刷新的高度(特殊的控件需要重寫該方法把篓,返回不同的數(shù)值)
    ///
    /// - Returns: 觸發(fā)刷新的高度
    open func willRefresHeight() -> CGFloat {
        return self.mj_h // 默認使用控件高度
    }
  • 當Loadding動畫顯示區(qū)域的高度和控件高度不一樣時重寫refreshingHoldHeight()单旁,如Demo里的:QQ
    /// Loadding動畫顯示區(qū)域的高度(特殊的控件需要重寫該方法,返回不同的數(shù)值)
    ///
    /// - Returns: Loadding動畫顯示區(qū)域的高度
    open func refreshingHoldHeight() -> CGFloat {
        return self.mj_h // 默認使用控件高度
    }

Example

//
//  TaoBaoRefreshHeader.swift
//  PullToRefreshKit
//
//  Created by luoyang on 10/12/16.
//  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(-M_PI/2),
                                        endAngle: CGFloat(M_PI_2 * 3),
                                        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: M_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
  • 必須實現(xiàn) SubGTMLoadMoreFooterProtocol

SubGTMLoadMoreFooterProtocol


public protocol SubGTMLoadMoreFooterProtocol {
    func toNormalState()
    func toNoMoreDataState()
    func toWillRefreshState()
    func toRefreshingState()

    /// 控件的高度(自定義控件通過該方法設置自定義高度)
    ///
    /// - Returns: 控件的高度
    func contentHeith() -> CGFloat
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巍沙,一起剝皮案震驚了整個濱河市葵姥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌句携,老刑警劉巖榔幸,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異矮嫉,居然都是意外死亡牡辽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門敞临,熙熙樓的掌柜王于貴愁眉苦臉地迎上來态辛,“玉大人,你說我怎么就攤上這事挺尿∽嗪冢” “怎么了炊邦?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熟史。 經(jīng)常有香客問我馁害,道長,這世上最難降的妖魔是什么蹂匹? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任碘菜,我火速辦了婚禮,結果婚禮上限寞,老公的妹妹穿的比我還像新娘忍啸。我一直安慰自己,他們只是感情好履植,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布计雌。 她就那樣靜靜地躺著,像睡著了一般玫霎。 火紅的嫁衣襯著肌膚如雪凿滤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天庶近,我揣著相機與錄音翁脆,去河邊找鬼。 笑死鼻种,一個胖子當著我的面吹牛反番,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播普舆,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼恬口,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沼侣?” 一聲冷哼從身側(cè)響起祖能,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛾洛,沒想到半個月后养铸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡轧膘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年钞螟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谎碍。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鳞滨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蟆淀,到底是詐尸還是另有隱情拯啦,我是刑警寧澤澡匪,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站褒链,受9級特大地震影響唁情,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甫匹,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一甸鸟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兵迅,春花似錦抢韭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽后雷。三九已至季惯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臀突,已是汗流浹背勉抓。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留候学,地道東北人藕筋。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像梳码,于是被迫代替她去往敵國和親隐圾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫掰茶、插件暇藏、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,029評論 4 62
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,515評論 25 707
  • 入伏以來氣溫居高不下,缺風少雨濒蒋,用電負荷屢創(chuàng)新高盐碱,電網(wǎng)設備面臨大考驗,部分設備出現(xiàn)問題沪伙,電力人在惡劣的高溫環(huán)境下保...
    西樓墨未濃閱讀 301評論 0 2