前言
本篇文章的提綱
1.舉一個例子扫茅,理解函數(shù)式編程
2.用函數(shù)式編程的思想,做一個可以隨意組合的動畫庫
什么是函數(shù)式編程畜份?
關于函數(shù)式編程诞帐,網(wǎng)上有很多欣尼。瀏覽眾多的文章后爆雹,對于函數(shù)式編程有了深刻的概念(其實就只記住了一句話): 函數(shù)是第一公民(函數(shù)可以當作參數(shù),返回值愕鼓「铺可以由多個低級的函數(shù)組合成一個高級的函數(shù))。
下面我們來看一個例子
在圖-1中菇晃,我們定義一個叫 AddGroup(加法組合) 的函數(shù) 册倒。
很多人第一眼看到就覺得這就是一個閉包或者block嘛,還故意打碼磺送,搞出一副很深奧的樣子驻子。
雖然這是一個block,但是不能把它理解為block估灿,因為當你認為這是block時崇呵,你就會想到回調。(反正我在網(wǎng)上看到這些例子的時候馅袁,就陷死在block的思維里了)域慷。
這里我們把它理解為函數(shù),或者公式,一套加法組合的公式犹褒。
在圖-2中抵窒,我們定義生成一個加法組合公式的func ,我暫時把實現(xiàn)的細節(jié)馬賽克了叠骑,因為我第一次看到這些例子的時候李皇,陷死在了那些實現(xiàn)的細節(jié)里。
接下來就是開始使用這些函數(shù)
再用個高級一點的
上圖-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所示睬隶。=> 這個符號表示锣夹,執(zhí)行完前一個動畫后再執(zhí)行下一個動畫。 + 加號符號表示 苏潜,兩個動畫一起執(zhí)行 银萍。 + 的優(yōu)先級高于 => , 先執(zhí)行加號 ,在執(zhí)行 =>恤左。
看一看執(zhí)行的效果
這算是動畫比較多的了贴唇,但是調用起來還是比較簡單,可讀性也比較高的飞袋。
在來一個:
代碼部分
首先我們定義一個要存儲動畫的類型
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
}
}
}