函數(shù)式編程之動畫組合

前言

本篇文章的提綱
1.舉一個例子扫茅,理解函數(shù)式編程
2.用函數(shù)式編程的思想,做一個可以隨意組合的動畫庫

什么是函數(shù)式編程畜份?

關于函數(shù)式編程诞帐,網(wǎng)上有很多欣尼。瀏覽眾多的文章后爆雹,對于函數(shù)式編程有了深刻的概念(其實就只記住了一句話): 函數(shù)是第一公民(函數(shù)可以當作參數(shù),返回值愕鼓「铺可以由多個低級的函數(shù)組合成一個高級的函數(shù))。

下面我們來看一個例子

在圖-1中菇晃,我們定義一個叫 AddGroup(加法組合) 的函數(shù) 册倒。


圖-1.png

很多人第一眼看到就覺得這就是一個閉包或者block嘛,還故意打碼磺送,搞出一副很深奧的樣子驻子。
雖然這是一個block,但是不能把它理解為block估灿,因為當你認為這是block時崇呵,你就會想到回調。(反正我在網(wǎng)上看到這些例子的時候馅袁,就陷死在block的思維里了)域慷。
這里我們把它理解為函數(shù),或者公式,一套加法組合的公式犹褒。

在圖-2中抵窒,我們定義生成一個加法組合公式的func ,我暫時把實現(xiàn)的細節(jié)馬賽克了叠骑,因為我第一次看到這些例子的時候李皇,陷死在了那些實現(xiàn)的細節(jié)里。

圖-2

接下來就是開始使用這些函數(shù)

圖-3

再用個高級一點的

圖-4

上圖-4中有一個叫compose的方法宙枷,作用是將add2和add3結合起來疙赠,組合成一個新的AddGroup。
第一個例子已經舉完朦拖,下面就是源代碼圃阳,大家可以直接復制粘貼到playground上去運行。在看下面源代碼前可以先嘗試著自己去實現(xiàn)璧帝。

//: Playground - noun: a place where people can play

import UIKit

typealias AddGroup = (Int)->Int //理解為的一系列加法組合的函數(shù)

func compose(_ addGroup1 : @escaping AddGroup , _ addGroup2 : @escaping AddGroup) -> AddGroup {
    return { a in
        return addGroup1(addGroup2(a))
    }
}

func add(_ num : Int) -> AddGroup {
    return { a in
        return a + num
    }
}

extension Int {
    //給Int擴展一個執(zhí)行AddGroup這個函數(shù)的方法
    func excute(addGroup : AddGroup) -> Int {
        return addGroup(self)
    }
}

let add2 : AddGroup = add(2)//生成一個叫add2(加2)的AddGroup
let add3 : AddGroup = add(3)//生成一個叫add3(加3)的AddGroup
let addNew : AddGroup = compose(add2, add3)
let result = 1.excute(addGroup: addNew)//讓1去執(zhí)行這個AddGroup捍岳,得到結果為6
print("結果為 :",result)

開始做一個可以任意組合的動畫庫

先看一看這個庫是如何調用的。

圖-5

如上圖-5所示睬隶。=> 這個符號表示锣夹,執(zhí)行完前一個動畫后再執(zhí)行下一個動畫。 + 加號符號表示 苏潜,兩個動畫一起執(zhí)行 银萍。 + 的優(yōu)先級高于 => , 先執(zhí)行加號 ,在執(zhí)行 =>恤左。

看一看執(zhí)行的效果

圖-6

這算是動畫比較多的了贴唇,但是調用起來還是比較簡單,可讀性也比較高的飞袋。

在來一個:

圖-7
代碼部分

首先我們定義一個要存儲動畫的類型

typealias AnimatState = (UIView) -> UIView

生成一個移動動畫的方法戳气,這里有個view.animCount , 我在后面會講。

func move(to point: CGPoint , with duration : TimeInterval) -> AnimatState {
    return { view in

        view.animCount += 1
        
        UIView.animate(withDuration: duration, delay: 0, options: [] , animations: {
            view.frame.origin = point
        }, completion: { _ in
            view.frame.origin = point
            view.animCount -= 1
        })
    
        return view
    }
}

生成透明巧鸭,旋轉瓶您,縮放一系列的動畫

透明
func fade(_ alpha: CGFloat , with duration : TimeInterval) -> AnimatState{
    return { view in
            
        view.animCount += 1
        
        UIView.animate(withDuration: duration, delay: 0, options: [] , animations: {
            view.alpha = alpha
        }, completion: { _ in
            view.alpha = alpha
            view.animCount -= 1
        })
        
        return view
    }
}
縮放
func scale(to scale: CGFloat , with duration : TimeInterval) -> AnimatState {
    return { view in
    
        view.animCount += 1
        
        UIView.animate(withDuration: duration, delay: 0, options: [] , animations: {
            view.transform = CGAffineTransform.init(scaleX: scale, y: scale)
        }, completion: { _ in
            view.transform = CGAffineTransform.init(scaleX: scale, y: scale)
            view.animCount -= 1
        })
        
        return view
    }
}
旋轉
func rotate(by angle: CGFloat , with duration : TimeInterval ) -> AnimatState {
    return { view in
        
        view.animCount += 1
        UIView.animate(withDuration: duration, delay: 0, options: [] , animations: {
            view.transform = .init(rotationAngle: angle)
        }, completion: { _ in
            view.transform = .init(rotationAngle: angle)
            view.animCount -= 1
        })
        
        return view
    }
}

再生成一個空動畫

func emptyAnim(_ duration : TimeInterval , closure : (()->Void)?) -> AnimatState{
    return { view in
    
        view.animCount += 1
        closure?()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration, execute: { 
            view.animCount -= 1
        })
        return view
    }
}

一些最基本的動畫就做完了。
接下來就開始做重載 + 操作符 , 加號表示的是將兩個動畫結合成一個能同時執(zhí)行這兩個小動畫的大動畫

func + (left : @escaping  AnimatState , right : @escaping AnimatState) -> AnimatState {
    return { view in
        return right(left(view))
    }
}

重載 => 操作符 纲仍,=> 表示的是先執(zhí)行左邊的呀袱,然后再執(zhí)行右邊的。

func => (left : @escaping AnimatState , right :@escaping AnimatState) -> AnimatState {
    return { view in
        view.animCompleteHandles.append( { [weak view] in
            guard view != nil else { return }
            _ = right(view!)
        })
      return left(view)
    }
}

animCompleteHandles 是UView extension 的一個用來存放動畫完成以后要做的動畫回調的數(shù)組郑叠。 看一看它的實現(xiàn)

fileprivate var anim_comptele_handle_key : Void!
extension UIView {
  var animCompleteHandles : [AnimComleteHandle] {
        set{
            objc_setAssociatedObject(self, &anim_comptele_handle_key, ( newValue as Any ), .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
        get{
            return (objc_getAssociatedObject(self, &anim_comptele_handle_key) as? [AnimComleteHandle]) ?? [AnimComleteHandle]()
        }
    }
    
}

接下來夜赵,我們還需要一個東西來判斷何時做完動畫。這里我用的是模仿引用計數(shù)的方式, 就是前面提到的view.animCount锻拘,當執(zhí)行一個動畫的時候油吭,計數(shù) +1 , 執(zhí)行執(zhí)行完后計數(shù) -1击蹲。當計數(shù)等于0時,我們要調用animCompleteHandles數(shù)組里面的最后一個元素進行回調婉宰,然后銷毀歌豺。

extension UIView {
    fileprivate var animCount : Int {
        set{
            
            if newValue < 0 {
                objc_setAssociatedObject(self, &anim_count_key, 0, .OBJC_ASSOCIATION_COPY_NONATOMIC)
                return 
            }
            
            objc_setAssociatedObject(self, &anim_count_key, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
            
            if newValue == 0 {
                animCompleteHandles.popLast()?()
            }
            
        }
        get{
            return ( objc_getAssociatedObject(self, &anim_count_key) as? Int ) ?? 0
        }
    }
}
獻上demo

https://github.com/Yuanjihua1/BuildingAnimation

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市心包,隨后出現(xiàn)的幾起案子类咧,更是在濱河造成了極大的恐慌,老刑警劉巖蟹腾,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痕惋,死亡現(xiàn)場離奇詭異,居然都是意外死亡娃殖,警方通過查閱死者的電腦和手機值戳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炉爆,“玉大人堕虹,你說我怎么就攤上這事》沂祝” “怎么了赴捞?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長郁稍。 經常有香客問我赦政,道長,這世上最難降的妖魔是什么耀怜? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任恢着,我火速辦了婚禮,結果婚禮上封寞,老公的妹妹穿的比我還像新娘然评。我一直安慰自己,他們只是感情好狈究,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盏求,像睡著了一般抖锥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碎罚,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天磅废,我揣著相機與錄音,去河邊找鬼荆烈。 笑死拯勉,一個胖子當著我的面吹牛竟趾,可吹牛的內容都是我干的。 我是一名探鬼主播宫峦,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼岔帽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了导绷?” 一聲冷哼從身側響起犀勒,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妥曲,沒想到半個月后贾费,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡檐盟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年褂萧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葵萎。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡箱玷,死狀恐怖,靈堂內的尸體忽然破棺而出陌宿,到底是詐尸還是另有隱情锡足,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布壳坪,位于F島的核電站舶得,受9級特大地震影響,放射性物質發(fā)生泄漏爽蝴。R本人自食惡果不足惜沐批,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蝎亚。 院中可真熱鬧九孩,春花似錦、人聲如沸发框。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梅惯。三九已至宪拥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铣减,已是汗流浹背她君。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留葫哗,地道東北人缔刹。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓球涛,卻偏偏與公主長得像孽鸡,于是被迫代替她去往敵國和親份乒。 傳聞我的和親對象是個殘疾皇子序芦,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容