如何使用 Swift 旋转核心文本?
How to Rotate Core Text with Swift?
我正在尝试使用 CoreGraphics 制作模拟时钟。
我的问题是我不知道如何旋转数字的文本。
代码的大致流程如下:
首先我旋转上下文,然后,如果适用,我画一个数字;重复。
完整的游乐场
import UIKit
import Foundation
import XCPlayground
class ClockFace: UIView {
func drawText(context: CGContextRef, text: NSString, attributes: [String: AnyObject], x: CGFloat, y: CGFloat) -> CGSize {
CGContextTranslateCTM(context, 0, 0)
CGContextScaleCTM(context, 1, -1)
let font = attributes[NSFontAttributeName] as! UIFont
let attributedString = NSAttributedString(string: text as String, attributes: attributes)
let textSize = text.sizeWithAttributes(attributes)
let textPath = CGPathCreateWithRect(CGRect(x: -x, y: y + font.descender, width: ceil(textSize.width), height: ceil(textSize.height)), nil)
let frameSetter = CTFramesetterCreateWithAttributedString(attributedString)
let frame = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: attributedString.length), textPath, nil)
CTFrameDraw(frame, context)
return textSize
}
override func drawRect(rect: CGRect) {
let ctx: CGContext? = UIGraphicsGetCurrentContext()
var nums: Int = 0
for i in 0..<60 {
CGContextSaveGState(ctx)
CGContextTranslateCTM(ctx, rect.width / 2, rect.height / 2)
CGContextRotateCTM(ctx, CGFloat(6.0 * Double(i) * M_PI / 180))
CGContextMoveToPoint(ctx, 72, 0)
if (i % 5 == 0) {
nums++
drawText(ctx!, text: "\(nums)", attributes: [NSForegroundColorAttributeName : UIColor.blackColor().CGColor, NSFontAttributeName : UIFont.systemFontOfSize(12)], x: 42, y: 0)
}
CGContextRestoreGState(ctx)
}
}
}
let myView = ClockFace(frame: CGRectMake(0, 0, 150, 150))
myView.backgroundColor = .lightGrayColor()
XCPShowView("", view: myView)
因此我的问题是,如何使用 Swift 旋转 Core Text?
您可以通过 CGContextSetTextMatrix()
实施 CGAffineTransform
来旋转文本。例如:
...
let frame = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: attributedString.length), textPath, nil)
CGContextSetTextMatrix(context, CGAffineTransformMakeRotation(CGFloat(M_PI)))
CTFrameDraw(frame, context)
这 15 行代码花了我 8 个多小时,大量的 Whosebug 搜索(许多答案没有't/no 长时间工作!),弄清楚。
当心使用 CTLineDraw 的解决方案。对于 CTLineDraw,您必须更改 cgContext.textMatrix 才能进行旋转,我得到了非常 strange/unreliable 的结果。生成一个框架然后旋转该框架证明更加可靠。
使用 Swift 5.2...
func drawCenteredString(
cgContext: CGContext,
string: String,
targetCenter: CGPoint,
angleClockwise: Angle,
uiFont: UIFont
) {
let attrString = NSAttributedString(string: string, attributes: [NSAttributedString.Key.font: uiFont])
let textSize = attrString.size()
let textPath = CGPath(rect: CGRect(x: CGFloat(-textSize.width/2), y: -uiFont.ascender / 2, width: ceil(textSize.width), height: ceil(textSize.height)), transform: nil)
let frameSetter = CTFramesetterCreateWithAttributedString(attrString)
let frame = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: attrString.length), textPath, nil)
cgContext.saveGState()
cgContext.translateBy(x: targetCenter.x, y: targetCenter.y)
cgContext.scaleBy(x: 1, y: -1)
cgContext.rotate(by: CGFloat(-angleClockwise.radians))
CTFrameDraw(frame, cgContext)
cgContext.restoreGState()
}
我正在尝试使用 CoreGraphics 制作模拟时钟。
我的问题是我不知道如何旋转数字的文本。
代码的大致流程如下:
首先我旋转上下文,然后,如果适用,我画一个数字;重复。
完整的游乐场
import UIKit
import Foundation
import XCPlayground
class ClockFace: UIView {
func drawText(context: CGContextRef, text: NSString, attributes: [String: AnyObject], x: CGFloat, y: CGFloat) -> CGSize {
CGContextTranslateCTM(context, 0, 0)
CGContextScaleCTM(context, 1, -1)
let font = attributes[NSFontAttributeName] as! UIFont
let attributedString = NSAttributedString(string: text as String, attributes: attributes)
let textSize = text.sizeWithAttributes(attributes)
let textPath = CGPathCreateWithRect(CGRect(x: -x, y: y + font.descender, width: ceil(textSize.width), height: ceil(textSize.height)), nil)
let frameSetter = CTFramesetterCreateWithAttributedString(attributedString)
let frame = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: attributedString.length), textPath, nil)
CTFrameDraw(frame, context)
return textSize
}
override func drawRect(rect: CGRect) {
let ctx: CGContext? = UIGraphicsGetCurrentContext()
var nums: Int = 0
for i in 0..<60 {
CGContextSaveGState(ctx)
CGContextTranslateCTM(ctx, rect.width / 2, rect.height / 2)
CGContextRotateCTM(ctx, CGFloat(6.0 * Double(i) * M_PI / 180))
CGContextMoveToPoint(ctx, 72, 0)
if (i % 5 == 0) {
nums++
drawText(ctx!, text: "\(nums)", attributes: [NSForegroundColorAttributeName : UIColor.blackColor().CGColor, NSFontAttributeName : UIFont.systemFontOfSize(12)], x: 42, y: 0)
}
CGContextRestoreGState(ctx)
}
}
}
let myView = ClockFace(frame: CGRectMake(0, 0, 150, 150))
myView.backgroundColor = .lightGrayColor()
XCPShowView("", view: myView)
因此我的问题是,如何使用 Swift 旋转 Core Text?
您可以通过 CGContextSetTextMatrix()
实施 CGAffineTransform
来旋转文本。例如:
...
let frame = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: attributedString.length), textPath, nil)
CGContextSetTextMatrix(context, CGAffineTransformMakeRotation(CGFloat(M_PI)))
CTFrameDraw(frame, context)
这 15 行代码花了我 8 个多小时,大量的 Whosebug 搜索(许多答案没有't/no 长时间工作!),弄清楚。
当心使用 CTLineDraw 的解决方案。对于 CTLineDraw,您必须更改 cgContext.textMatrix 才能进行旋转,我得到了非常 strange/unreliable 的结果。生成一个框架然后旋转该框架证明更加可靠。
使用 Swift 5.2...
func drawCenteredString(
cgContext: CGContext,
string: String,
targetCenter: CGPoint,
angleClockwise: Angle,
uiFont: UIFont
) {
let attrString = NSAttributedString(string: string, attributes: [NSAttributedString.Key.font: uiFont])
let textSize = attrString.size()
let textPath = CGPath(rect: CGRect(x: CGFloat(-textSize.width/2), y: -uiFont.ascender / 2, width: ceil(textSize.width), height: ceil(textSize.height)), transform: nil)
let frameSetter = CTFramesetterCreateWithAttributedString(attrString)
let frame = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: attrString.length), textPath, nil)
cgContext.saveGState()
cgContext.translateBy(x: targetCenter.x, y: targetCenter.y)
cgContext.scaleBy(x: 1, y: -1)
cgContext.rotate(by: CGFloat(-angleClockwise.radians))
CTFrameDraw(frame, cgContext)
cgContext.restoreGState()
}