I need a UILabel subcass with multiline attributed text with support for links, bold styles, etc. I also need tail truncation with an ellipsis. None of the open source co
Multi Line Vertical Glyph With Truncated. Swift3 and Swift4 version.
Add: Xcode9.1 Swift4 Compatibility. ( use block "#if swift(>=4.0)" )
class MultiLineVerticalGlyphWithTruncated: UIView, SimpleVerticalGlyphViewProtocol {
var text:String!
var font:UIFont!
var isVertical:Bool!
func setupProperties(text: String?, font:UIFont?, isVertical:Bool) {
self.text = text ?? ""
self.font = font ?? UIFont.systemFont(ofSize: UIFont.systemFontSize)
self.isVertical = isVertical
}
override func draw(_ rect: CGRect) {
if self.text == nil {
return
}
// Create NSMutableAttributedString
let attributed = NSMutableAttributedString(string: text) // if no ruby
//let attributed = text.attributedStringWithRuby() // if with ruby, Please create custom method
#if swift(>=4.0)
attributed.addAttributes([
NSAttributedStringKey.font: font,
NSAttributedStringKey.verticalGlyphForm: isVertical,
],
range: NSMakeRange(0, attributed.length))
#else
attributed.addAttributes([
kCTFontAttributeName as String: font,
kCTVerticalFormsAttributeName as String: isVertical,
],
range: NSMakeRange(0, attributed.length))
#endif
drawContext(attributed, textDrawRect: rect, isVertical: isVertical)
}
}
protocol SimpleVerticalGlyphViewProtocol {
}
extension SimpleVerticalGlyphViewProtocol {
func drawContext(_ attributed:NSMutableAttributedString, textDrawRect:CGRect, isVertical:Bool) {
guard let context = UIGraphicsGetCurrentContext() else { return }
var path:CGPath
if isVertical {
context.rotate(by: .pi / 2)
context.scaleBy(x: 1.0, y: -1.0)
path = CGPath(rect: CGRect(x: textDrawRect.origin.y, y: textDrawRect.origin.x, width: textDrawRect.height, height: textDrawRect.width), transform: nil)
}
else {
context.textMatrix = CGAffineTransform.identity
context.translateBy(x: 0, y: textDrawRect.height)
context.scaleBy(x: 1.0, y: -1.0)
path = CGPath(rect: textDrawRect, transform: nil)
}
let framesetter = CTFramesetterCreateWithAttributedString(attributed)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, nil)
// Check need for truncate tail
if (CTFrameGetVisibleStringRange(frame).length as Int) < attributed.length {
// Required truncate
let linesNS: NSArray = CTFrameGetLines(frame)
let linesAO: [AnyObject] = linesNS as [AnyObject]
var lines: [CTLine] = linesAO as! [CTLine]
let boundingBoxOfPath = path.boundingBoxOfPath
let lastCTLine = lines.removeLast()
let truncateString:CFAttributedString = CFAttributedStringCreate(nil, "\u{2026}" as CFString, CTFrameGetFrameAttributes(frame))
let truncateToken:CTLine = CTLineCreateWithAttributedString(truncateString)
let lineWidth = CTLineGetTypographicBounds(lastCTLine, nil, nil, nil)
let tokenWidth = CTLineGetTypographicBounds(truncateToken, nil, nil, nil)
let widthTruncationBegins = lineWidth - tokenWidth
if let truncatedLine = CTLineCreateTruncatedLine(lastCTLine, widthTruncationBegins, .end, truncateToken) {
lines.append(truncatedLine)
}
var lineOrigins = Array(repeating: CGPoint.zero, count: lines.count)
CTFrameGetLineOrigins(frame, CFRange(location: 0, length: lines.count), &lineOrigins)
for (index, line) in lines.enumerated() {
context.textPosition = CGPoint(x: lineOrigins[index].x + boundingBoxOfPath.origin.x, y:lineOrigins[index].y + boundingBoxOfPath.origin.y)
CTLineDraw(line, context)
}
}
else {
// Not required truncate
CTFrameDraw(frame, context)
}
}
}
How to use
class ViewController: UIViewController {
@IBOutlet weak var multiLineVerticalGlyphWithTruncated: MultiLineVerticalGlyphWithTruncated! // UIView
let font:UIFont = UIFont(name: "HiraMinProN-W3", size: 17.0) ?? UIFont.systemFont(ofSize: 17.0)
override func viewDidLoad() {
super.viewDidLoad()
let text = "iOS 11 sets a new standard for what is already the world’s most advanced mobile operating system. It makes iPhone better than before. It makes iPad more capable than ever. And now it opens up both to amazing possibilities for augmented reality in games and apps. With iOS 11, iPhone and iPad are the most powerful, personal, and intelligent devices they’ve ever been."
// If check for Japanese
// let text = "すでに世界で最も先進的なモバイルオペレーティングシステムであるiOSに、iOS 11が新たな基準を打ち立てます。iPhoneは今まで以上に優れたものになり、iPadはかつてないほどの能力を手に入れます。さらにこれからはどちらのデバイスにも、ゲームやアプリケーションの拡張現実のための驚くような可能性が広がります。iOS 11を搭載するiPhoneとiPadは、間違いなくこれまでで最もパワフルで、最もパーソナルで、最も賢いデバイスです。"
// if not vertical text, isVertical = false
multiLineVerticalGlyphWithTruncated.setupProperties(text: text, font: font, isVertical: true)
}
}