iOS中字迹动画效果

最近自己着手一款关于中国风的app,其中需要的一个想法就是诗词可以像ppt中的一种模式:字可以一个个的显示出来。最先的想法是将诗词分成一个个字放在一个个label上面,然后添加动画将其显示出来!然后这无疑是相当笨拙,且代码也是相当的丑陋!最后查阅了相关资料!终于找到了不错的解决方法!效果如下图所示

s24.gif
  • 废话不多说,直接上思路

如何将字迹给显示出来(重点)

首先我们要获取字的轨迹,然后才能做出相应的操作,比如给其添加path,添加动画等等。 这里有一个自己添加不是系统的字体,我就大概的说一下了。首先下载好ttf格式的字体,添加到项目中来,在info.plist中 添加Fonts provided by application 然后在下面添加对应的字体。

获取字体的轨迹

   let paths = CGPathCreateMutable() //创建一个变量path添加到贝瑟尔曲线
   let fontName = __CFStringMakeConstantString("MFTongXin_Noncommercial-Regular")//自己添加的字体,(貌似没有用,不知道什么鬼,难倒不支持系统外的?求解!)可以换成系统的字体
     print("\(fontName)")
   let fontRef:AnyObject = CTFontCreateWithName(fontName, 25, nil)
   let attrString = NSAttributedString(string: string, attributes: [kCTFontAttributeName as String : fontRef])
   let line = CTLineCreateWithAttributedString(attrString as CFAttributedString)
   let runA = CTLineGetGlyphRuns(line)

  • 上面的runA获取了所有的字迹的line CFArrayGetCount(runA)获取一个数组。遍历数组 去除每个元素 (其中涉及到了一些与c桥接的内容 估计有的会比较陌生,慢慢看,还是很好懂的)
       let run = CFArrayGetValueAtIndex(runA, runIndex);
       let runb = unsafeBitCast(run, CTRun.self)
       let  CTFontName = unsafeBitCast(kCTFontAttributeName, UnsafePointer<Void>.self)
       let runFontC = CFDictionaryGetValue(CTRunGetAttributes(runb),CTFontName)
       let runFontS = unsafeBitCast(runFontC, CTFont.self)
       let width = UIScreen.mainScreen().bounds.width
       
       var temp = 0
       var offset:CGFloat = 0.0
  • unsafeBitCast是非常危险的操作,它会将一个指针指向的内存强制按位转换为目标的类型。因为这种转换是在Swift的类型管理之外进行的,因此编译器无法确保得到的类型是否确实正确,你必须明确地知道你在做什么。

遍历每个字迹,在这边可以修改横竖的方向

      //在这边修改应该可以修改横竖的方向
      for(var i = 0; i < CTRunGetGlyphCount(runb); i++){
          let range = CFRangeMake(i, 1)
          let glyph:UnsafeMutablePointer<CGGlyph> = UnsafeMutablePointer<CGGlyph>.alloc(1)//在Swift中不能像C里那样使用&符号直接获取地址来进行操作。如果我们想对某个变量进行指针操作,我们可以借助withUnsafePointer这个辅助方法。
          glyph.initialize(0)
          let position:UnsafeMutablePointer<CGPoint> = UnsafeMutablePointer<CGPoint>.alloc(1)
          position.initialize(CGPointZero)
          CTRunGetGlyphs(runb, range, glyph)
          CTRunGetPositions(runb, range, position);
          
          let temp3 = CGFloat(position.memory.x)
          let temp2 = (Int) (temp3 / width)
          let temp1 = 0
          if(temp2 > temp1){
              temp = temp2
              offset = position.memory.x - (CGFloat(temp) * width)
          }
          let path = CTFontCreatePathForGlyph(runFontS,glyph.memory,nil)
          let x = position.memory.x - (CGFloat(temp) * width) - offset
          let y = position.memory.y - (CGFloat(temp) * 80)
          var transform = CGAffineTransformMakeTranslation(x, y)
          CGPathAddPath(paths, &transform, path)
          glyph.destroy()
          glyph.dealloc(1)
          position.destroy()
          position.dealloc(1)  //销毁操作
      }

将paths添加到贝瑟尔曲线

       let bezierPath = UIBezierPath()
       bezierPath.moveToPoint(CGPointZero)
       bezierPath.appendPath(UIBezierPath(CGPath: paths))
~~~


- 好了最主要的事情已经OK了下面就可以为这个动画添加点酷炫的效果了,比如现在流行的渐变色。

~~~
    private var gradientLayer = CAGradientLayer() //创建全局变量
~~~

添加渐变色的layer和动画

~~~
1.添加颜色
        // 渐变色的颜色数
        let count = 10
        var colors:[CGColorRef] = []
        let topColor = UIColor(red: (91/255.0), green: (91/255.0), blue: (91/255.0), alpha: 1)
        let buttomColor = UIColor(red: (24/255.0), green: (24/255.0), blue: (24/255.0), alpha: 1)
        let gradientColors: [CGColor] = [topColor.CGColor, buttomColor.CGColor]
        for _ in 0 ..< count {
            let color = UIColor.init(red: CGFloat(arc4random() % 256) / 255, green: CGFloat(arc4random() % 256) / 255, blue: CGFloat(arc4random() % 256) / 255, alpha: 1.0)
            colors.append(color.CGColor)
        }
~~~

~~~
添加方向
        gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
        gradientLayer.colors = gradientColors
        gradientLayer.frame = self.bounds
        gradientLayer.type = kCAGradientLayerAxial
        self.layer.addSublayer(gradientLayer)
添加动画
        // 渐变色的动画
        let animation = CABasicAnimation(keyPath: "colors")
        animation.duration = 0.5
        animation.repeatCount = MAXFLOAT
        var toColors:[CGColorRef] = []
        for _ in 0 ..< count {
            let color = UIColor.init(red: CGFloat(arc4random() % 256) / 255, green: CGFloat(arc4random() % 256) / 255, blue: CGFloat(arc4random() % 256) / 255, alpha: 1.0)
            toColors.append(color.CGColor)
        }
        animation.autoreverses = true
        animation.toValue = toColors
        gradientLayer.addAnimation(animation, forKey: "gradientLayer")

~~~

最后将笔迹的path添加到layer上面去,将其以动画的形式显示出来 然后讲pathLayer添加到渐变色的layer的mask属性
~~~
        gradientLayer.mask = pathLayer
~~~

####最终在ViewController调用
~~~
这里用了一个延时的线程,里面的时间可以根据你动画的时间自己来算
        let viewPoem = PoemShow(frame: CGRectMake(0, 200, self.view.frame.size.width, 50), message: "楚城今近远,")
         view.addSubview(viewPoem)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5*Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
            let viewPoem1 = PoemShow(frame: CGRectMake(0, 250, self.view.frame.size.width, 50), message: "积霭寒塘暮。")
            self.view.addSubview(viewPoem1)
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5*Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
                let viewPoem2 = PoemShow(frame: CGRectMake(0, 300, self.view.frame.size.width, 50), message: "水浅舟且迟,")
                self.view.addSubview(viewPoem2)
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5*Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
                    let viewPoem3 = PoemShow(frame: CGRectMake(0, 350, self.view.frame.size.width, 50), message: "淮潮至何处。")
                    self.view.addSubview(viewPoem3)
                }
            }
        }

~~~

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

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,469评论 6 30
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 3,016评论 0 21
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,058评论 4 62
  • 生活中,人们常常觉得颈椎疼,却很少拿它当回事,也不刻意保护颈椎。如果保护不好颈椎,整个身体都会生病。 颈椎不好疾病...
    我微微一笑阅读 921评论 0 2
  • 说好不再联系的,听到他说他时不时有轻生的念头时,还是莫名地心疼。 我不知道这算不算爱?之前那么恨、现在反倒自责了?...
    沙漏在梦游阅读 156评论 0 0