我正在寻找一些最新的帮助/提示,了解如何使用Swift2
代表iOS9
在圆的边缘绘制简单的单线字符串.我看到了相当陈旧的例子,涉及旧的ObjC片段,通常仅限于OS X
个.在定制UIView
子类的drawRect()
方法中,这在iOS中是可能的吗?
我正在寻找一些最新的帮助/提示,了解如何使用Swift2
代表iOS9
在圆的边缘绘制简单的单线字符串.我看到了相当陈旧的例子,涉及旧的ObjC片段,通常仅限于OS X
个.在定制UIView
子类的drawRect()
方法中,这在iOS中是可能的吗?
我想说"你试过什么?",但现在是周五下午,我很早就下班了,所以我利用这个机会翻译了我的旧ObjC代码.就在这里,适合playground .把它放在你的视野中应该是微不足道的.
Swift 2
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, context: CGContextRef, radius r:CGFloat, angle theta: CGFloat, colour c: UIColor, font: UIFont, 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, -1)
centreArcPerpendicularText("Hello round world", context: context, radius: 100, angle: 0, colour: UIColor.redColor(), font: UIFont.systemFontOfSize(16), clockwise: true)
centreArcPerpendicularText("Anticlockwise", context: context, radius: 100, angle: CGFloat(-M_PI_2), colour: UIColor.redColor(), font: UIFont.systemFontOfSize(16), clockwise: false)
centreText("Hello flat world", context: context, radius: 0, angle: 0 , colour: UIColor.yellowColor(), font: UIFont.systemFontOfSize(16), slantAngle: CGFloat(M_PI_4))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
输出为:
更新
更新Swift 3
func centreArcPerpendicular(text str: String, context: CGContext, 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]
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, 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"
centre(text: 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 centre(text str: String, context: CGContext, radius r:CGFloat, angle theta: CGFloat, colour c: UIColor, font: UIFont, 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
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, 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...
// *******************************************************************
context.translateBy (x: size.width / 2, y: size.height / 2)
context.scaleBy (x: 1, y: -1)
centreArcPerpendicular(text: "Hello round world", context: context, radius: 100, angle: 0, colour: UIColor.red, font: UIFont.systemFont(ofSize: 16), clockwise: true)
centreArcPerpendicular(text: "Anticlockwise", context: context, radius: 100, angle: CGFloat(-M_PI_2), colour: UIColor.red, font: UIFont.systemFont(ofSize: 16), clockwise: false)
centre(text: "Hello flat world", context: context, radius: 0, angle: 0 , colour: UIColor.yellow, font: UIFont.systemFont(ofSize: 16), slantAngle: CGFloat(M_PI_4))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Swift 4
import UIKit
func centreArcPerpendicular(text str: String, context: CGContext, 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 characters: [String] = str.map { String($0) } // An array of single character strings, each character in str
let l = characters.count
let attributes = [NSAttributedStringKey.font: font]
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(withAttributes: 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: CGFloat = clockwise ? -.pi / 2 : .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], 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 centre(text str: String, context: CGContext, radius r: CGFloat, angle theta: CGFloat, colour c: UIColor, font: UIFont, 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 = [NSAttributedStringKey.foregroundColor: c, NSAttributedStringKey.font: font]
//let attributes = [NSForegroundColorAttributeName: c, 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(withAttributes: 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, 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...
// *******************************************************************
context.translateBy (x: size.width / 2, y: size.height / 2)
context.scaleBy(x: 1, y: -1)
centreArcPerpendicular(text: "Hello round ? world", context: context, radius: 100, angle: 0, colour: UIColor.red, font: UIFont.systemFont(ofSize: 16), clockwise: true)
centreArcPerpendicular(text: "Anticlockwise", context: context, radius: 100, angle: CGFloat(-M_PI_2), colour: UIColor.red, font: UIFont.systemFont(ofSize: 16), clockwise: false)
centre(text: "Hello flat world", context: context, radius: 0, angle: 0 , colour: UIColor.yellow, font: UIFont.systemFont(ofSize: 16), slantAngle: .pi / 4)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Update to show use in UIView
comments 员@RitVikupadhayaya询问如何在UIView
米的范围内做到这一点——这对老手来说是显而易见的,但对初学者来说可能不是.诀窍是使用UIGraphicsGetCurrentContext
without调用UIGraphicsBeginImageContextWithOptions
(将UIView
的上下文替换为current上下文)获得正确的上下文,因此您的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", context: context, radius: 100, angle: 0, colour: UIColor.red, font: UIFont.systemFont(ofSize: 16), clockwise: true)
centreArcPerpendicular(text: "Anticlockwise", context: context, radius: 100, angle: CGFloat(-M_PI_2), colour: UIColor.red, font: UIFont.systemFont(ofSize: 16), clockwise: false)
centre(text: "Hello flat world", context: context, radius: 0, angle: 0 , colour: UIColor.yellow, font: UIFont.systemFont(ofSize: 16), slantAngle: CGFloat(M_PI_4))
}
}