2017-07-24 55 views
4

讓我們想象一下,我有一些文字:「在梯形部分歸因文字如何繪製梯形文字(或圓圈)?

我有NSAttributedString擴展,它返回我的UIImage,與歸屬文字:

extension NSAttributedString { 
    func asImage() -> UIImage? { 
     defer { 
      UIGraphicsEndImageContext() 
     } 
     let size = boundingRect(with: CGSize.zero, options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil).size 
     UIGraphicsBeginImageContext(size) 
     draw(at: CGPoint.zero) 
     return UIGraphicsGetImageFromCurrentImageContext() 
    } 
} 

但這個函數返回我的文字在一行中,因爲使用boundingRect的,:

------------------------------------ 
|Some attributed text in trapezoid.| 
------------------------------------ 

如果我想使用的繪製文本定製矩形就會忍不住多...

UIGraphicsBeginImageContext(CGRect(x: 0, y: 0, width: 100, height: 30)) 
draw(at: CGPoint.zero) 

...因爲文本將在矩形:

-------------- 
|Some attribu| 
|ted text in | 
|trapezoid. | 
-------------- 

我需要什麼,是繪製文本的梯形與已知的角落位置(或已知半徑的圓)。因此,文本的每一新行應該有一點偏移量,請看例子開始: enter image description here

所以我希望看到這樣的事情:

--------------- 
\Some attribut/ 
\ed text in/
    \trapezoid/ 
    --------- 

我怎樣才能實現這個結果?

+1

本文提供了一個良好的開端。 https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html#//apple_ref/doc/uid/TP40009542-CH4-SW1 – BallpointBen

+1

對於基於圓形的形狀,我認爲這裏是你的解決方案:[如何在UILabel中將文本放入一個圓圈](https://stackoverflow.com/a/22179104/2124535)並將它用於梯形*不應*(未測試)因爲這意味着將排除的區域定義爲2個直角三角形 – nathan

+1

添加到BallPointBen中,也許有一些想法:https://stackoverflow.com/questions/26632474/uitextview-textcontainer-exclusion-path-fails-if-full-width-and -position-at-to-to- – Larme

回答

1

您必須在此處下拉到CoreText級別。好的新功能是,你將能夠以任何你想要的形狀繪製文本!

extension NSAttributedString { 
    public func draw(in path: CGPath) { 
     let context = UIGraphicsGetCurrentContext()! 

     let transform = CGAffineTransform(scaleX: +1, y: -1) 

     let flippedPath = CGMutablePath() 
     flippedPath.addPath(path, transform: transform) 

     let range = CFRange(location: 0, length: 0) 
     let framesetter = CTFramesetterCreateWithAttributedString(self) 
     let frame = CTFramesetterCreateFrame(framesetter, range, flippedPath, nil) 

     context.saveGState() 

     // Debug: fill path. 
     context.setFillColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5) 
     context.beginPath() 
     context.addPath(path) 
     context.fillPath() 

     context.concatenate(transform) 

     CTFrameDraw(frame, context) 

     context.restoreGState() 
    } 
} 

而且你可以使用它像這樣:

let string = NSAttributedString(string: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.") 

let bounds = CGRect(x: 0, y: 0, width: 120, height: 120) 

let path = CGMutablePath() 
path.move(to: CGPoint(x: 10, y: 10)) 
path.addLine(to: CGPoint(x: 110, y: 10)) 
path.addLine(to: CGPoint(x: 90, y: 110)) 
path.addLine(to: CGPoint(x: 30, y: 110)) 
path.closeSubpath() 

UIGraphicsBeginImageContextWithOptions(bounds.integral.size, true, 0) 
defer { UIGraphicsEndImageContext() } 

let context = UIGraphicsGetCurrentContext()! 
context.setFillColor(UIColor.white.cgColor) 
context.fill(.infinite) 

string.draw(in: path) 

let image = UIGraphicsGetImageFromCurrentImageContext()! 

壞消息是,這個方案不會給你一個最後的省略號。如果你確實想要這樣做,你可能需要對製版機給你的最後一行進行一些調整。

+0

很酷,這就是我需要的。 「壞消息是,這個解決方案最後並沒有給你一個省略號 - 如果路徑是橢圓的,它也適用於橢圓。你知道如何爲中心設置水平文本對齊嗎? –

+0

@VasiliiMuravev你可以嘗試使用屬性字符串的段落風格。如果這不起作用,您可以通過向框架索取線條並單獨繪製線條來自己水平居中。 –

0

隨着Christian的Schnorr answer使得它爲我的目的:

import PlaygroundSupport 
import UIKit 

extension NSAttributedString { 

    /// Draws attributed string in selected CGPath. 
    func draw(in path: CGPath) { 
     guard let context = UIGraphicsGetCurrentContext() else { return } 
     let transform = CGAffineTransform(scaleX: 1.0, y: -1.0) 
     let flippedPath = CGMutablePath() 
     flippedPath.addPath(path, transform: transform) 
     let range = CFRange(location: 0, length: 0) 
     let framesetter = CTFramesetterCreateWithAttributedString(self) 
     let frame = CTFramesetterCreateFrame(framesetter, range, flippedPath, nil) 
     context.saveGState() 
     context.concatenate(transform) 
     CTFrameDraw(frame, context) 
     context.restoreGState() 
    } 

    /// Renders attributed string. 
    /// 
    /// - Parameters: 
    /// - size: A 'CGSize' for rendering string in trapezoid. 
    /// - degree: A `CGFloat`, representing trapezoid angles in degrees. 
    /// - Returns: An optional `UIImage` with rendered string. 
    func asTrapezoidImage(size: CGSize, degree: CGFloat) -> UIImage? { 
     UIGraphicsBeginImageContextWithOptions(size, false, 0) 
     defer { UIGraphicsEndImageContext() } 
     draw(in: size.trapezoidPath(degree)) 
     return UIGraphicsGetImageFromCurrentImageContext() 
    } 
} 

extension CGSize { 

    /// Converts CGSize into trapezoid CGPath. 
    /// 
    /// - Parameter degree: A `CGFloat`, representing trapezoid angles in degrees. 
    /// - Returns: A `CGPath` with trapezoid. 
    func trapezoidPath(_ degree: CGFloat) -> CGPath { 
     var offset = height * tan(CGFloat.pi * degree/180.0) 
     offset = max(0, min(width/2.0, offset)) 
     let path = CGMutablePath() 
     path.move(to: CGPoint.zero) 
     path.addLine(to: CGPoint(x: width, y: 0.0)) 
     path.addLine(to: CGPoint(x: width - offset, y: height)) 
     path.addLine(to: CGPoint(x: offset, y: height)) 
     path.closeSubpath() 
     return path 
    } 
} 

使用:

let string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit." 
let attributes: [NSAttributedStringKey: Any] = [ 
    .foregroundColor: UIColor.blue, 
    .backgroundColor: UIColor.white, 
    .font: UIFont.systemFont(ofSize: 24.0) 
] 
let attrString = NSAttributedString(string: string, attributes: attributes) 
let size = CGSize(width: 400.0, height: 120.0) 
let image = attrString.asTrapezoidImage(size: size, degree: 12.0) 
let imageView = UIImageView(image: image) 
imageView.frame = CGRect(origin: CGPoint.zero, size: size) 
PlaygroundPage.current.needsIndefiniteExecution = false 
PlaygroundPage.current.liveView = imageView 

注意度從0°允許90°