UITableView和UICollectionView實現(xiàn)自定義滾動動畫

前言

看到這個標題你可能會覺得“這不是很簡單嗎阵苇?像下面這么一寫就完了唄”

UIView.animate(withDuration: 0.25) {
    self.tableView.setContentOffset(CGPoint(x: 0, y: 500), animated: false)
}

不不不,如果你覺得就這么簡單的話說明你還是太年輕了悴晰。這樣寫你的列表在滾動的一開始上面的cell就消失了,這種效果是完全過不了產(chǎn)品和視覺小姐姐的像素眼。

為了實現(xiàn)自定義滾動動效我們可以使用CADisplayLink來實現(xiàn)瓤介,至于為什么不用其他timer相信大家可以自己百度了解。

當然如果你并不想那么麻煩的自己寫的話可以使用Facebook出品的Pop動畫庫赘那,它也是基于CADisplayLink實現(xiàn)的刑桑。由于UIScrollView的滾動原理,我們可以用POPBasicAnimation設置UIScrollView的bounds屬性動畫即可募舟。

Demo

自定義列表滾動動效

實現(xiàn)

Animator

首先我們先實現(xiàn)實際動畫的類ScrollViewAnimator祠斧。

private class ScrollViewAnimator {
    
    weak var scrollView: UIScrollView?
    let timingFunction: ScrollTimingFunction
    
    var closure: (() -> Void)?
    
    //動畫開始時間
    var startTime: TimeInterval = 0
    //動畫初始的contentOffset
    var startOffset: CGPoint = .zero
    //動畫目標的contentOffset
    var destinationOffset: CGPoint = .zero
    //動畫時長
    var duration: TimeInterval = 0
    //動畫已運行時長
    var runTime: TimeInterval = 0
    
    var timer: CADisplayLink?
    
    init(scrollView: UIScrollView, timingFunction: ScrollTimingFunction) {
        self.scrollView = scrollView
        self.timingFunction = timingFunction
    }
    
    func setContentOffset(_ contentOffset: CGPoint, duration: TimeInterval) {
        guard let scrollView = scrollView else {
            return
        }
        //設置需要的屬性
        startTime = Date().timeIntervalSince1970
        startOffset = scrollView.contentOffset
        destinationOffset = contentOffset
        self.duration = duration
        runTime = 0
        guard self.duration > 0 else {
            scrollView.setContentOffset(contentOffset, animated: false)
            return
        }
        if timer == nil {
            timer = CADisplayLink(target: self, selector: #selector(animtedScroll))
            //把timer加入到common的runloop中
            timer?.add(to: .main, forMode: .common)
        }
    }
    
    @objc
    func animtedScroll() {
        guard let timer = timer else { return }
        guard let scrollView = scrollView else { return }
        //由于CADisplayLink每次回調(diào)的時間不固定突琳,所以使用它自己記錄的回調(diào)時間來增加運行時長
        runTime += timer.duration
        if runTime >= duration {
            //如果運行時長超過動畫時長說明動畫需要結束了
            scrollView.setContentOffset(destinationOffset, animated: false)
            timer.invalidate()
            self.timer = nil
            closure?()
            return
        }
        
        var offset = scrollView.contentOffset
        offset.x = timingFunction.compute(CGFloat(runTime), startOffset.x, destinationOffset.x - startOffset.x, CGFloat(duration))
        offset.y = timingFunction.compute(CGFloat(runTime), startOffset.y, destinationOffset.y - startOffset.y, CGFloat(duration))
        scrollView.setContentOffset(offset, animated: false)
    }
    
}

UIScrollView拓展

我們用OC的runtime知識動態(tài)為分類添加屬性方便使用

public extension UIScrollView {
    
    private struct AssociatedKeys {
        static var animator: String = "animator"
    }
    
    private var animator: ScrollViewAnimator? {
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.animator, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.animator) as? ScrollViewAnimator
        }
    }
    
    func setContentOffset(_ contentOffset: CGPoint, duration: TimeInterval, timingFunction: ScrollTimingFunction = .linear, completion: (() -> Void)? = nil) {
        if animator == nil {
            animator = ScrollViewAnimator(scrollView: self, timingFunction: timingFunction)
        }
        animator!.closure = { [weak self] in
            guard let strongSelf = self else { return }
            DispatchQueue.main.async {
                strongSelf.animator = nil
            }
            completion?()
        }
        animator!.setContentOffset(contentOffset, duration: duration)
    }
    
}

ScrollTimingFunction枚舉

動畫緩沖函數(shù)的實現(xiàn)可以參考http://robertpenner.com/easing/猛计。具體實現(xiàn)例子在Demo

參考

http://robertpenner.com/easing/
https://blog.csdn.net/S_clifftop/article/details/89490422
https://zsisme.gitbooks.io/ios-/content/index.html

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝴猪,一起剝皮案震驚了整個濱河市嘹害,隨后出現(xiàn)的幾起案子拟糕,更是在濱河造成了極大的恐慌对途,老刑警劉巖卒蘸,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件校辩,死亡現(xiàn)場離奇詭異填抬,居然都是意外死亡烛芬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赘娄,“玉大人仆潮,你說我怎么就攤上這事∏簿剩” “怎么了性置?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長揍堰。 經(jīng)常有香客問我鹏浅,道長,這世上最難降的妖魔是什么屏歹? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任隐砸,我火速辦了婚禮,結果婚禮上蝙眶,老公的妹妹穿的比我還像新娘季希。我一直安慰自己,他們只是感情好幽纷,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布式塌。 她就那樣靜靜地躺著,像睡著了一般霹崎。 火紅的嫁衣襯著肌膚如雪珊搀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天尾菇,我揣著相機與錄音境析,去河邊找鬼。 笑死派诬,一個胖子當著我的面吹牛劳淆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播默赂,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼沛鸵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缆八?” 一聲冷哼從身側響起曲掰,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奈辰,沒想到半個月后栏妖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡奖恰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年吊趾,在試婚紗的時候發(fā)現(xiàn)自己被綠了宛裕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡论泛,死狀恐怖揩尸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屁奏,我是刑警寧澤岩榆,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站了袁,受9級特大地震影響朗恳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜载绿,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望油航。 院中可真熱鬧崭庸,春花似錦、人聲如沸谊囚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镰踏。三九已至函筋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奠伪,已是汗流浹背跌帐。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绊率,地道東北人谨敛。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像滤否,于是被迫代替她去往敵國和親脸狸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

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

  • 在市運動會的志愿者活動上藐俺,姜賦認識了宋晗之炊甲,宋晗之是經(jīng)管學院的學生會主席又是活動的組長,負責任有耐心欲芹,不耍官威卿啡,不...
    莉莉伯特閱讀 411評論 4 3
  • 第一次聽說這本書的書名,是在給初一女兒 開家長會的時候語文老師提到的耀石,當時她說孩子們今年要讀的書單中有這本書...
    葉檸海閱讀 128評論 0 0
  • 《日精進打卡》 姓名:倪顯忠 公司名稱:上海松科 《六項精進》學習營第499期感謝一組隊長 《幸福精進》學習營第6...
    d94b51782f34閱讀 378評論 0 0
  • 第一次覺得在一起那么累……
    ha哈哈嗝閱讀 125評論 0 1