(IOS) 向Hero致敬與分析 (二) - Double研究所

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

  • Plugin
  • heroModifierString

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


public func animateTransition(using context: UIViewControllerContextTransitioning) {
 /*
 略
.
*/
 start()
}

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

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同时属于这两类。

 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),之后所有动画都在上面进行,算是最好懂的一块。

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指令。

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)"

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顶层看看另外一个扩充变数,

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

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

apply:(inout HeroTargetState) -> Void

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容