最近自己著手一款關(guān)于中國風(fēng)的app,其中需要的一個想法就是詩詞可以像ppt中的一種模式:字可以一個個的顯示出來。最先的想法是將詩詞分成一個個字放在一個個label上面,然后添加動畫將其顯示出來!然后這無疑是相當笨拙,且代碼也是相當?shù)某舐?!最后查閱了相關(guān)資料!終于找到了不錯的解決方法!效果如下圖所示

s24.gif
- 廢話不多說,直接上思路
如何將字跡給顯示出來(重點)
首先我們要獲取字的軌跡,然后才能做出相應(yīng)的操作,比如給其添加path,添加動畫等等。 這里有一個自己添加不是系統(tǒng)的字體,我就大概的說一下了。首先下載好ttf格式的字體,添加到項目中來,在info.plist中 添加Fonts provided by application 然后在下面添加對應(yīng)的字體。
獲取字體的軌跡
let paths = CGPathCreateMutable() //創(chuàng)建一個變量path添加到貝瑟爾曲線
let fontName = __CFStringMakeConstantString("MFTongXin_Noncommercial-Regular")//自己添加的字體,(貌似沒有用,不知道什么鬼,難倒不支持系統(tǒng)外的?求解?。┛梢該Q成系統(tǒng)的字體
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)獲取一個數(shù)組。遍歷數(shù)組 去除每個元素 (其中涉及到了一些與c橋接的內(nèi)容 估計有的會比較陌生,慢慢看,還是很好懂的)
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是非常危險的操作,它會將一個指針指向的內(nèi)存強制按位轉(zhuǎn)換為目標的類型。因為這種轉(zhuǎn)換是在Swift的類型管理之外進行的,因此編譯器無法確保得到的類型是否確實正確,你必須明確地知道你在做什么。
遍歷每個字跡,在這邊可以修改橫豎的方向
//在這邊修改應(yīng)該可以修改橫豎的方向
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))
~~~
- 好了最主要的事情已經(jīng)OK了下面就可以為這個動畫添加點酷炫的效果了,比如現(xiàn)在流行的漸變色。
~~~
private var gradientLayer = CAGradientLayer() //創(chuàng)建全局變量
~~~
添加漸變色的layer和動畫
~~~
1.添加顏色
// 漸變色的顏色數(shù)
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調(diào)用
~~~
這里用了一個延時的線程,里面的時間可以根據(jù)你動畫的時間自己來算
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的相關(guān)解決方法,不足之處,請多多指出!如果有更好的解決方法,也要分享下哦。