本篇继上篇: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" ) plugins = Hero .enabledPlugins.map({ return $0 .init () }) } }
一开头就是我们没见过的plugins 这个变数,所以我们先来浅谈plugins 是什么。
Plugin是Hero提供可以让使用者、开发者自行在做自定义的处理动画或是其他变数处理。只要让类别实作HeroPlugin(继承HeroPreprocessor, HeroAnimator两个协定 )这个类别,即可在过场的进行时,收到以下通知:
现在我们来看看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 animators = Hero .builtInAnimator for plugin in plugins { processors.append(plugin) animators.append(plugin) } 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 animatingViewContainer = UIView (frame: transitionContainer.bounds) transitionContainer.addSubview(animatingViewContainer) 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 ]() for r in matches(for: modifiersRegex, text:modifierString) { var parameters = [String ]() if r.numberOfRanges > 2 , r.rangeAt(2 ).location < modifierString.length { let parameterString = modifierString.substring(with: r.rangeAt(2 )) as NSString for r in matches(for: parameterRegex, text: parameterString){ parameters.append(parameterString.substring(with: r.range)) } } let name = modifierString.substring(with: r.rangeAt(1 )) 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)呼叫。