我正在寻找一些关于如何使用
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)) } }