Skip to content

Instantly share code, notes, and snippets.

@cemolcay
Created January 22, 2017 22:03
Show Gist options
  • Save cemolcay/9525d28a203da83d333545fc6e0d4371 to your computer and use it in GitHub Desktop.
Save cemolcay/9525d28a203da83d333545fc6e0d4371 to your computer and use it in GitHub Desktop.
Draws a curved string on a CALayer with angle, radius and text that you give.
import UIKit
// swift port of stackoverflow answer
// http://stackoverflow.com/a/31301238/2048130
extension CGFloat {
/** Degrees to Radian **/
var degrees: CGFloat {
return self * (180.0 / .pi)
}
/** Radians to Degrees **/
var radians: CGFloat {
return self / 180.0 * .pi
}
}
func drawCurvedString(on layer: CALayer, text: NSAttributedString, angle: CGFloat, radius: CGFloat) {
var radAngle = angle.radians
let textSize = text.boundingRect(
with: CGSize(width: .max, height: .max),
options: [.usesLineFragmentOrigin, .usesFontLeading],
context: nil)
.integral
.size
let perimeter: CGFloat = 2 * .pi * radius
let textAngle: CGFloat = textSize.width / perimeter * 2 * .pi
var textRotation: CGFloat = 0
var textDirection: CGFloat = 0
if angle > CGFloat(10).radians, angle < CGFloat(170).radians {
// bottom string
textRotation = 0.5 * .pi
textDirection = -2 * .pi
radAngle += textAngle / 2
} else {
// top string
textRotation = 1.5 * .pi
textDirection = 2 * .pi
radAngle -= textAngle / 2
}
for c in 0..<text.length {
let letter = text.attributedSubstring(from: NSRange(c..<c+1))
let charSize = letter.boundingRect(
with: CGSize(width: .max, height: .max),
options: [.usesLineFragmentOrigin, .usesFontLeading],
context: nil)
.integral
.size
let letterAngle = (charSize.width / perimeter) * textDirection
let x = radius * cos(radAngle + (letterAngle / 2))
let y = radius * sin(radAngle + (letterAngle / 2))
let singleChar = drawText(
on: layer,
text: letter,
frame: CGRect(
x: (layer.frame.size.width / 2) - (charSize.width / 2) + x,
y: (layer.frame.size.height / 2) - (charSize.height / 2) + y,
width: charSize.width,
height: charSize.height))
layer.addSublayer(singleChar)
singleChar.transform = CATransform3DMakeAffineTransform(CGAffineTransform(rotationAngle: radAngle - textRotation))
radAngle += letterAngle
}
}
func drawText(on layer: CALayer, text: NSAttributedString, frame: CGRect) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.frame = frame
textLayer.string = text
textLayer.alignmentMode = kCAAlignmentCenter
textLayer.contentsScale = UIScreen.main.scale
return textLayer
}
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
drawCurvedString(
on: view.layer,
text: NSAttributedString(
string: "This is a test string",
attributes: [
NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName: UIFont.systemFont(ofSize: 15)
]),
angle: 0,
radius: 85)
@MAndonovski991
Copy link

Thanks for sharing,
I would like to share one maybe bug maybe feature :),
line 34
if angle > CGFloat(10).radians, angle < CGFloat(170).radians {
I think this gonna be always "false"
Instead of angle in degree we should use angle in radians so we can have rotated string in the bottom half

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment