本篇繼上篇:http://m.itdecent.cn/p/fdab69f7440a
本篇種點(diǎn):
- Plugin
- heroModifierString
1.3.1)Present流程 - start() - Plugin, HeroPreprocessor , HeroAnimator
public func animateTransition(using context: UIViewControllerContextTransitioning) {
/*
略
.
*/
start()
}
我們來(lái)到最主要的程式段:start()
func start() {
plugins == nil {
print("plugins == nil")
//把enabledPlugins里的每個(gè)類別全部init后放進(jìn)
plugins = Hero.enabledPlugins.map({ return $0.init() })
}
/*
略
.
*/
}
一開(kāi)頭就是我們沒(méi)見(jiàn)過(guò)的plugins這個(gè)變數(shù),所以我們先來(lái)淺談plugins是什么。
Plugin是Hero提供可以讓使用者、開(kāi)發(fā)者自行在做自定義的處理動(dòng)畫或是其他變數(shù)處理。只要讓類別實(shí)作HeroPlugin(繼承HeroPreprocessor, HeroAnimator兩個(gè)協(xié)定)這個(gè)類別,即可在過(guò)場(chǎng)的進(jìn)行時(shí),收到以下通知:
Hero's Plugin
現(xiàn)在我們來(lái)看看HeroPreprocessor, HeroAnimator
Hero有以下兩個(gè)變數(shù),可以把它們想成兩條線程,一條專門做一系列的HeroPreprocessor, 而另一條則做一系列的HeroAnimator。上面提到的Plugin同時(shí)屬于這兩類。
var processors: [HeroPreprocessor]!
var animators: [HeroAnimator]!
func start() {
/* 略 */
processors = Hero.builtInProcessors //Hero內(nèi)建程序
animators = Hero.builtInAnimator //Hero內(nèi)建動(dòng)畫
// 如果有插件的話就加進(jìn)程序
for plugin in plugins {
processors.append(plugin)
animators.append(plugin)
}
/* 略 */
// ask each preprocessor to process
for processor in processors {
processor.process(context:context, fromViews: context.fromViews, toViews: context.toViews)
}
/* 略 */
for animator in animators.reversed() {
let currentFromViews = fromViews.filterInPlace{ [context] (view:UIView) -> Bool in
return !animator.canAnimate(context: context!, view: view, appearing: false)
}
let currentToViews = toViews.filterInPlace{ [context] (view:UIView) -> Bool in
return !animator.canAnimate(context: context!, view: view, appearing: true)
}
animatorViews.insert((currentFromViews, currentToViews), at: 0)
}
}
1.3.2)Present流程 - start() - 制作過(guò)場(chǎng)"舞臺(tái)"
復(fù)制一個(gè)起始view(from view)蓋上去(animatingViewContainer),之后所有動(dòng)畫都在上面進(jìn)行,算是最好懂的一塊。
func start() {
/* 略 */
transitionContainer.isUserInteractionEnabled = false
// a view to hold all the animation views
//transitionContainer是在context拿到的
animatingViewContainer = UIView(frame: transitionContainer.bounds)
transitionContainer.addSubview(animatingViewContainer) //加上一個(gè)空的等大容器
// create a snapshot view to hide all the flashing that might happen
let completeSnapshot = fromView.snapshotView(afterScreenUpdates: true)!
transitionContainer.addSubview(completeSnapshot)
animatingViewContainer.addSubview(fromView)
animatingViewContainer.insertSubview(toView, belowSubview: fromView)
animatingViewContainer.backgroundColor = toView.backgroundColor
/* 略 */
context = HeroContext(container:animatingViewContainer, fromView: fromView, toView:toView)
}
1.4)HeroContext
這個(gè)類別算是Hero的特色也是最有技巧性的一部分。主要處理這兩個(gè) "HeroID", "heroModifierString"
1.4.1) heroModifierString
一句話概括它,就是“指令”,指示這個(gè)View要執(zhí)行怎樣的過(guò)場(chǎng)動(dòng)畫。使用者在StoryBoard或是swift里給定ModifierString后,即在變數(shù)的set{...}將字串轉(zhuǎn)為可執(zhí)行的function指令。
public extension UIView{
/* 略 */
@IBInspectable public var heroModifierString: String? {
/* 略 */
let modifierString = newValue as NSString
var modifiers = [HeroModifier]() //function結(jié)果
//modifiersRegex = "(\\w+)(?:\\(([^\\)]*)\\))?"
for r in matches(for: modifiersRegex, text:modifierString) //A Loop
{
var parameters = [String]()
if r.numberOfRanges > 2, r.rangeAt(2).location < modifierString.length
{
let parameterString = modifierString.substring(with: r.rangeAt(2)) as NSString
//parameterRegex = "(?:\\-?\\d+(\\.?\\d+)?)|\\w+"
for r in matches(for: parameterRegex, text: parameterString){ //B Loop
parameters.append(parameterString.substring(with: r.range))
}
}
let name = modifierString.substring(with: r.rangeAt(1))
//取得Function
if let modifier = HeroModifier.from(name: name, parameters: parameters){
modifiers.append(modifier)
/*略*/
numberOfRanges:regx的組數(shù),最外層()的數(shù)量。
rangeAt(0) = 有符合的結(jié)果, rangeAt(1) = 有一組以上,rangeAt(2) = 有兩組以上......
A Loop: 找出 一個(gè)以上非空白 可能出現(xiàn)一對(duì)括號(hào) 中間夾著多個(gè)非右括號(hào) = 一個(gè)Fuction的格式 , 若 numberOfRanges > 2 則代表有(參數(shù))。
B Loop: 參數(shù) 可是一串非空白 或是 數(shù)子(可負(fù)數(shù)或小數(shù))
EX: heroModifierString = zPosition(2) arc
parameterString = 2 , name = zPosition
name = arc
讀出個(gè)別指令后就是利用一個(gè)配對(duì)Function,可以看到最終每個(gè)字串有配對(duì)結(jié)果的話,可以得到一個(gè)個(gè)的"閉包(Closure)"
public class HeroModifier {
internal let apply:(inout HeroTargetState) -> Void
public init(applyFunction:@escaping (inout HeroTargetState) -> Void){
apply = applyFunction
}
/*略*/
static func from(name:String, parameters:[String]) -> HeroModifier?
{
switch name {
case "zPosition":
if let zPosition = parameters.getCGFloat(0){ //拿參數(shù)
modifier = .zPosition(zPosition)
}
}
/*略*/
public static func zPosition(_ zPosition:CGFloat) -> HeroModifier {
return HeroModifier { targetState in
targetState.zPosition = zPosition
}
}
}
/*略*/
public static func arc(intensity:CGFloat = 1) -> HeroModifier {
return HeroModifier { targetState in
targetState.arc = intensity
}
}
走到這么深之后,我們現(xiàn)在在回extension UIView頂層看看另外一個(gè)擴(kuò)充變數(shù),
public extension UIView{
/*略*/
public var heroModifiers: [HeroModifier]?
現(xiàn)在我們可以把它理解成一個(gè)可執(zhí)行的閉包集合,因?yàn)槊總€(gè)HeroModifier都自帶一個(gè)閉包
apply:(inout HeroTargetState) -> Void
它記著一個(gè)View在過(guò)場(chǎng)時(shí),所有要執(zhí)行的過(guò)場(chǎng)動(dòng)畫,可利用
modifier.apply(&HeroTargetState)呼叫。
