ios開發(fā)文字內(nèi)描邊效果

實現(xiàn)了外描邊效果之后,產(chǎn)品問內(nèi)描邊好做嗎,我說簡單!
簡單個蛋!


圖片發(fā)自簡書App

因為蘋果本身的描邊效果,是雙向描邊,不是單側(cè)的,見上一篇,而且發(fā)現(xiàn)也無法用外描邊的方式實現(xiàn).
想了很久,最后想的實現(xiàn)方式是,拿到每一個字符的path,然后用字體本身的大小畫出來,再用比本身小一點的筆刷,在上面疊加一層細(xì)一些的文字,間接實現(xiàn)內(nèi)描邊的效果.
理論是沒問題的
然后用coretext,拿到ctframe,再用ctFrame獲取到每一行的文字CTline,再用CTLine獲取到CTRun(一段相同富文本的合集),再用CTrun獲取到每一個字的字形CGGlyph.
代碼如下

let letters = CGMutablePath.init()
let framesetterNew = CTFramesetterCreateWithAttributedString(attributeString)
let frameNew = CTFramesetterCreateFrame(framesetterNew, CFRangeMake(0, CFAttributedStringGetLength(attributeString)), path, nil)
let lineArr = CTFrameGetLines(frameNew)
let lineCount = CFArrayGetCount(lineArr)
let _lineOrigins = UnsafeMutablePointer<CGPoint>.allocate(capacity: MemoryLayout<CGPoint>.size * lineCount)
CTFrameGetLineOrigins(frameNew, CFRange(location: 0, length: 0), _lineOrigins)
for lineIndex in 0 ..<  lineCount{
    let linePointer = CFArrayGetValueAtIndex(lineArr, lineIndex)
    let line = unsafeBitCast(linePointer, to: CTLine.self)
    let runArray = CTLineGetGlyphRuns(line)
    for index in 0 ..< CFArrayGetCount(runArray) {
        let runPointer = CFArrayGetValueAtIndex(runArray, index)
        let run = unsafeBitCast(runPointer, to: CTRun.self)
        let runFont = unsafeBitCast(CFDictionaryGetValue(CTRunGetAttributes(run),unsafeBitCast(kCTFontAttributeName, to: UnsafePointer.self)),to: CTFont.self)
        for glyphIndex in 0 ..< CTRunGetGlyphCount(run){
            let thisGlyphRange = CFRangeMake(glyphIndex, 1)
            let glyphPointer = UnsafeMutablePointer<CGGlyph>.allocate(capacity: MemoryLayout<CGGlyph>.size)
            let position = UnsafeMutablePointer<CGPoint>.allocate(capacity: MemoryLayout<CGPoint>.size)
            CTRunGetGlyphs(run, thisGlyphRange, glyphPointer)
            CTRunGetPositions(run, thisGlyphRange, position)
            let letterPath = CTFontCreatePathForGlyph(runFont, glyphPointer.pointee, nil)
            let transT = CGAffineTransform.init(translationX:  _lineOrigins[lineIndex].x + position.pointee.x, y:  position.pointee.y + _lineOrigins[lineIndex].y)
            if letterPath != nil{
                letters.addPath(letterPath!, transform: transT)
            }
            free(glyphPointer)
            free(position)
        }
    }
}
ctx.saveGState()
ctx.addPath(letters)
ctx.setStrokeColor(UIColor.blue.cgColor)//無用顏色
let color = attributeString.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor
ctx.setFillColor(color!.cgColor) //設(shè)置字體本身顏色
ctx.closePath()
ctx.fillPath()
ctx.addPath(letters)
print(abs(strokeWidth))
ctx.setLineWidth(abs(strokeWidth))
ctx.setBlendMode(CGBlendMode.clear)
ctx.strokePath()
ctx.setBlendMode(CGBlendMode.destinationAtop)
var newAtt = NSAttributedString.init(attributedString: attributeString)
newAtt = newAtt.appendAddAttributes([NSAttributedString.Key.foregroundColor:self.strokeColor])
let framesettera = CTFramesetterCreateWithAttributedString(newAtt)
let framea = CTFramesetterCreateFrame(framesettera, CFRangeMake(0, CFAttributedStringGetLength(newAtt)), path, nil)
CTFrameDraw(framea, ctx)
ctx.restoreGState()

這樣就拿到了所有文字的path!搞定!


圖片發(fā)自簡書App

運行效果如下,跟想的完全不一樣


圖片發(fā)自簡書App

瞬間我就慌了,我可是看了整整一天的coretext。能不能用拿到的這個東西搞事情呢,畢竟花了一整天時間,這個時候,就顯示出智慧的重要性了.曲線救國

首先將獲取到的path用fill模式畫一遍,這就是原本的文字

ctx.saveGState() //先保存context的狀態(tài),用于畫完描邊之后恢復(fù)
ctx.addPath(letters)
ctx.setFillColor(color!.cgColor) //設(shè)置字體本身顏色
ctx.closePath()
ctx.fillPath()

然后重點來了,再畫一遍剛才鏤空的那個path,然后用粗線條畫,

圖片發(fā)自簡書App

這樣和原本的畫好的文字做疊加,選擇clear模式,這樣兩者重疊的部分顏色就會被清理掉,代碼

ctx.addPath(letters)
print(abs(strokeWidth))
ctx.setLineWidth(abs(10))
ctx.setBlendMode(CGBlendMode.clear)
ctx.strokePath()

效果如下


圖片發(fā)自簡書App

這樣就得到了,內(nèi)描邊效果里面顯示的內(nèi)容

我真他娘的是個天才
原本想的是得到的這個瘦弱的圖形畫在,本身原本的字體上,就完成效果了
然后坑又來了
clearmode,會把之前所有已經(jīng)畫好的顏色,清除掉,這樣無論最下層原本的文字什么顏色,都會被裁剪到,第二部瘦弱字體的那個大小。這一點我忘得死死地


圖片發(fā)自簡書App

后來想了兩個方案,一個是新建一個context,在這個context上面畫圖,然后再把這個context push進(jìn)棧,然后;兩者混合
另外一個是將coretext得到的path做一個translate弄到別處畫,畫好了再clip掉圖形,再放到原本的位置,這樣也不受clear影響.
但是兩個都只是猜測.不一定成.

后來,想了想有沒有,將圖層上下顛倒顯示的疊加模式呢,就是原本綠色在下面,紅色在上面,混合后紅色在下面,綠色在上面?
還真他娘的有
CGBlendMode.destinationAtop
這樣我先clear模式得到需要的瘦弱圖形,然后再畫原本文字蓋在上面,本來的效果是原本的文字完全的蓋住了瘦小的那個文字.然后用這個疊加模式顏色顛倒后,效果就是內(nèi)描邊效果了.
真費勁吶.網(wǎng)上還沒有搜到先例.
上代碼

ctx.saveGState()
ctx.addPath(letters)
ctx.setStrokeColor(UIColor.blue.cgColor)//無用顏色
let color = attributeString.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor
ctx.setFillColor(color!.cgColor) //設(shè)置字體本身顏色
ctx.closePath()
ctx.fillPath()
ctx.addPath(letters)
print(abs(strokeWidth))
ctx.setLineWidth(abs(strokeWidth))
ctx.setBlendMode(CGBlendMode.clear)
ctx.strokePath()
ctx.setBlendMode(CGBlendMode.destinationAtop)
var newAtt = NSAttributedString.init(attributedString: attributeString)
newAtt = newAtt.appendAddAttributes([NSAttributedString.Key.foregroundColor:self.strokeColor])
let framesettera = CTFramesetterCreateWithAttributedString(newAtt)
let framea = CTFramesetterCreateFrame(framesettera, CFRangeMake(0, CFAttributedStringGetLength(newAtt)), path, nil)
CTFrameDraw(framea, ctx)
ctx.restoreGState()

最終效果

內(nèi)描邊
圖片發(fā)自簡書App

外描邊
圖片發(fā)自簡書App
圖片發(fā)自簡書App

另一種實現(xiàn)方式,得到字形path后clip,這樣超出path區(qū)域的地方就會被裁掉,再stroke的時候直接就是內(nèi)描邊效果了
辦法總比困難多

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • blog.csdn.net CoreText實現(xiàn)圖文混排 - 博客頻道 CoreText實現(xiàn)圖文混排 也好久沒來寫...
    K_Gopher閱讀 702評論 0 0
  • 蘋果文檔 https://developer.apple.com/documentation/coretext C...
    陽明AI閱讀 520評論 0 4
  • 最近在網(wǎng)上看了一些大牛的文章,自己也試著寫了一下,感覺圖文混排真的很強大。 廢話不多說,開始整 先上效果圖跟代碼,...
    AllureJM閱讀 1,121評論 0 1
  • CoreText是一個進(jìn)階的比較底層的布局文本和處理字體的技術(shù),CoreText API在OS X v10.5 和...
    smalldu閱讀 13,809評論 18 129
  • iOS沒有現(xiàn)成的支持圖文混排的控件,而要用多個基礎(chǔ)控件組合拼成圖文混排這樣復(fù)雜的排版,是件很苦逼的事情。對此的解決...
    清風(fēng)沐沐閱讀 745評論 0 2

友情鏈接更多精彩內(nèi)容