向Hero致敬與分析-(二)

本篇继上篇:http://www.jianshu.com/p/fdab69f7440a
本篇重点:

  • Plugin
  • heroModifierString

1.3.1)Present流程 - start() - Plugin, HeroPreprocessor , HeroAnimator

1
2
3
4
5
6
7
public func animateTransition(using context: UIViewControllerContextTransitioning) {
/*

.
*/
start()
}

我们来到最主要的程式段:start()

1
2
3
4
5
6
7
8
9
10
11
func start() {
plugins == nil {
print("plugins == nil")
//把enabledPlugins里的每个类别全部init后放进
plugins = Hero.enabledPlugins.map({ return $0.init() })
}
/*

.
*/
}

一开头就是我们没见过的plugins这个变数,所以我们先来浅谈plugins是什么。

Plugin是Hero提供可以让使用者、开发者自行在做自定义的处理动画或是其他变数处理。只要让类别实作HeroPlugin(继承HeroPreprocessor, HeroAnimator两个协定)这个类别,即可在过场的进行时,收到以下通知:
Hero's Plugin


现在我们来看看HeroPreprocessor, HeroAnimator
Hero有以下两个变数,可以把它们想成两条线程,一条专门做一系列的HeroPreprocessor, 而另一条则做一系列的HeroAnimator。上面提到的Plugin同时属于这两类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 var processors: [HeroPreprocessor]!
var animators: [HeroAnimator]!

func start() {
/* 略 */
processors = Hero.builtInProcessors //Hero内建程序
animators = Hero.builtInAnimator //Hero内建动画

// 如果有插件的话就加进程序
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() - 制作过场”舞台”

复制一个起始view(from view)盖上去(animatingViewContainer),之后所有动画都在上面进行,算是最好懂的一块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func start() {
/* 略 */
transitionContainer.isUserInteractionEnabled = false
// a view to hold all the animation views
//transitionContainer是在context拿到的
animatingViewContainer = UIView(frame: transitionContainer.bounds)
transitionContainer.addSubview(animatingViewContainer) //加上一个空的等大容器
// 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

这个类别算是Hero的特色也是最有技巧性的一部分。主要处理这两个 “HeroID”, “heroModifierString”

1.4.1) heroModifierString

一句话概括它,就是“指令”,指示这个View要执行怎样的过场动画。使用者在StoryBoard或是swift里给定ModifierString后,即在变数的set{…}将字串转为可执行的function指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public extension UIView{
/* 略 */
@IBInspectable public var heroModifierString: String? {
/* 略 */
let modifierString = newValue as NSString
var modifiers = [HeroModifier]() //function结果
//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的组数,最外层()的数量。

rangeAt(0) = 有符合的结果, rangeAt(1) = 有一组以上,rangeAt(2) = 有两组以上……

A Loop: 找出 一个以上非空白 可能出现一对括号 中间夹着多个非右括号 = 一个Fuction的格式 , 若 numberOfRanges > 2 则代表有(参数)。
B Loop: 参数 可是一串非空白 或是 数子(可负数或小数)

EX: heroModifierString = zPosition(2) arc

parameterString = 2 , name = zPosition
name = arc

读出个别指令后就是利用一个配对Function,可以看到最终每个字串有配对结果的话,可以得到一个个的**”闭包(Closure)”**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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){ //拿参数
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
}
}

走到这么深之后,我们现在在回extension UIView顶层看看另外一个扩充变数,

1
2
3
public extension UIView{
/*略*/
public var heroModifiers: [HeroModifier]?

现在我们可以把它理解成一个可执行的闭包集合,因为每个HeroModifier都自带一个闭包

apply:(inout HeroTargetState) -> Void

它记着一个View在过场时,所有要执行的过场动画,可利用
modifier.apply(&HeroTargetState)呼叫。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×