在Swift for iOS中沿循循环径绘制文字

我正在寻找一些关于如何使用 Swift2 for iOS9在一个圆圈边缘绘制简单的单行字符串的最新帮助/提示.我看到很旧的例子涉及旧的ObjC片段,而且仅限于OS X.这是否可以在iOS中使用自定义UIView子类的drawRect()方法?
我要说“你试了什么?”,但星期五下午我早点下班了,所以我借机翻译了我的旧ObjC代码.这里适合游乐场.把它放在你的UIView中应该是微不足道的.

斯威夫特2
请参阅下面的Swift 3更新…

import UIKit

func centreArcPerpendicularText(str: String,context: CGContextRef,radius r: CGFloat,angle theta: CGFloat,colour c: UIColor,font: UIFont,clockwise: Bool){
    // *******************************************************
    // This draws the String str around an arc of radius r,// with the text centred at polar angle theta
    // *******************************************************

    let l = str.characters.count
    let attributes = [NSFontAttributeName: font]

    var characters: [String] = [] // This will be an array of single character strings,each character in str
    var arcs: [CGFloat] = [] // This will be the arcs subtended by each character
    var totalArc: CGFloat = 0 // ... and the total arc subtended by the string

    // Calculate the arc subtended by each letter and their total
    for i in 0 ..< l {
        characters += [String(str[str.startIndex.advancedBy(i)])]
        arcs += [chordToArc(characters[i].sizeWithAttributes(attributes).width,radius: r)]
        totalArc += arcs[i]
    }

    // Are we writing clockwise (right way up at 12 o'clock,upside down at 6 o'clock)
    // or anti-clockwise (right way up at 6 o'clock)?
    let direction: CGFloat = clockwise ? -1 : 1
    let slantCorrection = clockwise ? -CGFloat(M_PI_2) : CGFloat(M_PI_2)

    // The centre of the first character will then be at
    // thetaI = theta - totalArc / 2 + arcs[0] / 2
    // But we add the last term inside the loop
    var thetaI = theta - direction * totalArc / 2

    for i in 0 ..< l {
        thetaI += direction * arcs[i] / 2
        // Call centerText with each character in turn.
        // Remember to add +/-90º to the slantAngle otherwise
        // the characters will "stack" round the arc rather than "text flow"
        centreText(characters[i],context: context,radius: r,angle: thetaI,colour: c,font: font,slantAngle: thetaI + slantCorrection)
        // The centre of the next character will then be at
        // thetaI = thetaI + arcs[i] / 2 + arcs[i + 1] / 2
        // but again we leave the last term to the start of the next loop...
        thetaI += direction * arcs[i] / 2
    }
}

func chordToArc(chord: CGFloat,radius: CGFloat) -> CGFloat {
    // *******************************************************
    // Simple geometry
    // *******************************************************
    return 2 * asin(chord / (2 * radius))
}

func centreText(str: String,radius r:CGFloat,slantAngle: CGFloat) {
    // *******************************************************
    // This draws the String str centred at the position
    // specified by the polar coordinates (r,theta)
    // i.e. the x= r * cos(theta) y= r * sin(theta)
    // and rotated by the angle slantAngle
    // *******************************************************

    // Set the text attributes
    let attributes = [NSForegroundColorAttributeName: c,NSFontAttributeName: font]
    // Save the context
    CGContextSaveGState(context)
    // Undo the inversion of the Y-axis (or the text goes backwards!)
    CGContextScaleCTM(context,1,-1)
    // Move the origin to the centre of the text (negating the y-axis manually)
    CGContextTranslateCTM(context,r * cos(theta),-(r * sin(theta)))
    // Rotate the coordinate system
    CGContextRotateCTM(context,-slantAngle)
    // Calculate the width of the text
    let offset = str.sizeWithAttributes(attributes)
    // Move the origin by half the size of the text
    CGContextTranslateCTM (context,-offset.width / 2,-offset.height / 2) // Move the origin to the centre of the text (negating the y-axis manually)
    // Draw the text
    str.drawAtPoint(CGPointZero,withAttributes: attributes)
    // Restore the context
    CGContextRestoreGState(context)
}

// *******************************************************
// Playground code to test
// *******************************************************
let size = CGSize(width: 256,height: 256)

UIGraphicsBeginImageContextWithOptions(size,true,0.0)
let context = UIGraphicsGetCurrentContext()!
// *******************************************************************
// Scale & translate the context to have 0,0
// at the centre of the screen maths convention
// Obviously change your origin to suit...
// *******************************************************************
CGContextTranslateCTM (context,size.width / 2,size.height / 2)
CGContextScaleCTM (context,-1)

centreArcPerpendicularText("Hello round world",radius: 100,angle: 0,colour: UIColor.redColor(),font: UIFont.systemFontOfSize(16),clockwise: true)
centreArcPerpendicularText("Anticlockwise",angle: CGFloat(-M_PI_2),clockwise: false)
centreText("Hello flat world",radius: 0,colour: UIColor.yellowColor(),slantAngle: CGFloat(M_PI_4))


let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

输出为:

更新
顺时针/逆时针方向直的例子

更新Swift 3

func centreArcPerpendicular(text str: String,context: CGContext,// with the text centred at polar angle theta
    // *******************************************************

    let l = str.characters.count
    let attributes = [NSFontAttributeName: font]

    let characters: [String] = str.characters.map { String($0) } // An array of single character strings,each character in str
    var arcs: [CGFloat] = [] // This will be the arcs subtended by each character
    var totalArc: CGFloat = 0 // ... and the total arc subtended by the string

    // Calculate the arc subtended by each letter and their total
    for i in 0 ..< l {
        arcs += [chordToArc(characters[i].size(attributes: attributes).width,upside down at 6 o'clock)
    // or anti-clockwise (right way up at 6 o'clock)?
    let direction: CGFloat = clockwise ? -1 : 1
    let slantCorrection = clockwise ? -CGFloat(M_PI_2) : CGFloat(M_PI_2)

    // The centre of the first character will then be at
    // thetaI = theta - totalArc / 2 + arcs[0] / 2
    // But we add the last term inside the loop
    var thetaI = theta - direction * totalArc / 2

    for i in 0 ..< l {
        thetaI += direction * arcs[i] / 2
        // Call centerText with each character in turn.
        // Remember to add +/-90º to the slantAngle otherwise
        // the characters will "stack" round the arc rather than "text flow"
        centre(text: characters[i],slantAngle: thetaI + slantCorrection)
        // The centre of the next character will then be at
        // thetaI = thetaI + arcs[i] / 2 + arcs[i + 1] / 2
        // but again we leave the last term to the start of the next loop...
        thetaI += direction * arcs[i] / 2
    }
}

func chordToArc(_ chord: CGFloat,radius: CGFloat) -> CGFloat {
    // *******************************************************
    // Simple geometry
    // *******************************************************
    return 2 * asin(chord / (2 * radius))
}

func centre(text str: String,NSFontAttributeName: font]
    // Save the context
    context.saveGState()
    // Undo the inversion of the Y-axis (or the text goes backwards!)
    context.scaleBy(x: 1,y: -1)
    // Move the origin to the centre of the text (negating the y-axis manually)
    context.translateBy(x: r * cos(theta),y: -(r * sin(theta)))
    // Rotate the coordinate system
    context.rotate(by: -slantAngle)
    // Calculate the width of the text
    let offset = str.size(attributes: attributes)
    // Move the origin by half the size of the text
    context.translateBy (x: -offset.width / 2,y: -offset.height / 2) // Move the origin to the centre of the text (negating the y-axis manually)
    // Draw the text
    str.draw(at: CGPoint(x: 0,y: 0),withAttributes: attributes)
    // Restore the context
    context.restoreGState()
}

// *******************************************************
// Playground code to test
// *******************************************************
let size = CGSize(width: 256,0
// at the centre of the screen maths convention
// Obviously change your origin to suit...
// *******************************************************************
context.translateBy (x: size.width / 2,y: size.height / 2)
context.scaleBy (x: 1,y: -1)

centreArcPerpendicular(text: "Hello round world",colour: UIColor.red,font: UIFont.systemFont(ofSize: 16),clockwise: true)
centreArcPerpendicular(text: "Anticlockwise",clockwise: false)
centre(text: "Hello flat world",colour: UIColor.yellow,slantAngle: CGFloat(M_PI_4))


let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

更新以在UIView中显示使用

评论员@RitvikUpadhyaya询问如何在UIView中做到这一点 – 对老手来说显而易见,但不是初学者.诀窍是使用UIGraphicsGetCurrentContext获取正确的上下文,而无需调用UIGraphicsBeginImageContextWithOptions(将UIView的上下文替换为当前上下文) – 因此您的UIView应该如下所示:

class MyView: UIView {
    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        let size = self.bounds.size

        context.translateBy (x: size.width / 2,y: size.height / 2)
        context.scaleBy (x: 1,y: -1)

        centreArcPerpendicular(text: "Hello round world",clockwise: true)
        centreArcPerpendicular(text: "Anticlockwise",clockwise: false)
        centre(text: "Hello flat world",slantAngle: CGFloat(M_PI_4))
    }
}

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...