ルビを振る の名詠式を Swift4 で書いたもの。
- 行頭約物をちゃんと処理したい
- 連続したダッシュは仮想ボディではなくて字面体でくっついてるように見せたい
- Keinez は斜体が正しいはずなので直したい
みたいな思いがあったのですが、このリビジョンでは正規表現を extension を使わない方法に変えたので、上の辺りはまったく未処理です。
Unmanaged
の使い方が毎回よく分からなくなりますね。
import UIKit | |
let text = """ | |
「まさか、|後罪《クライム》の|触媒《カタリスト》を〈|讃来歌《オラトリオ》〉無しで?」 | |
|教師《きょうし》たちの|狼狽《ろうばい》した声が次々と上がる。 | |
……なんでだろう。何を驚いているんだろう。 | |
ただ|普通《ふつう》に、この|触媒《カタリスト》を使って|名詠門《チャネル》を開かせただけなのに。 | |
そう言えば、何を|詠《よ》ぼう。 | |
自分の一番好きな花でいいかな。 | |
どんな|宝石《ほうせき》より|素敵《すてき》な、わたしの大好きな|緋色《ひいろ》の花。 | |
――『|Keinez《赤》』―― | |
そして、少女の口ずさんだその後に―― | |
""" | |
@IBDesignable class View: UIView { | |
lazy var textAttributes: [NSAttributedStringKey: Any] = { | |
let style = NSMutableParagraphStyle() | |
style.minimumLineHeight = 24 | |
style.maximumLineHeight = 24 | |
return [.font: UIFont(name: "HiraMinProN-W3", size: 18.0)!, | |
.verticalGlyphForm: true, | |
.paragraphStyle: style] | |
}() | |
var attributedText: NSAttributedString { | |
let workingAttributedText = NSMutableAttributedString(string: text, attributes: textAttributes) | |
// 名前付き正規表現は iOS11+ じゃないと無理です | |
let rubyRegex = try! NSRegularExpression(pattern: "|(?<string>.+?)《(?<ruby>.+?)》", options: []) | |
for result in rubyRegex.matches(in: text, options: [], range: NSRange(location: 0, length: text.count)).reversed() { | |
guard | |
let stringRange = Range(result.range(withName: "string"), in: text), | |
let rubyRange = Range(result.range(withName: "ruby"), in: text) | |
else { | |
continue | |
} | |
let string = String(text[stringRange]) | |
let ruby = String(text[rubyRange]) | |
workingAttributedText.replaceCharacters(in: result.range, | |
with: createRuby(string: string, ruby: ruby)) | |
} | |
return workingAttributedText | |
} | |
private func createRuby(string: String, ruby: String) -> NSAttributedString { | |
var unmanage = Unmanaged.passRetained(ruby as CFString) | |
defer { unmanage.release() } | |
var text: [Unmanaged<CFString>?] = [unmanage, .none, .none, .none] | |
let annotation = CTRubyAnnotationCreate(.auto, .auto, 0.5, &text) | |
let attributedString = NSMutableAttributedString(string: string, | |
attributes: [kCTRubyAnnotationAttributeName as NSAttributedStringKey: annotation]) | |
attributedString.addAttributes(textAttributes, range: NSRange(location: 0, length: string.count)) | |
return attributedString | |
} | |
override func draw(_ rect: CGRect) { | |
guard let context = UIGraphicsGetCurrentContext() else { return } | |
context.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) | |
context.addRect(rect) | |
context.fillPath() | |
context.rotate(by: CGFloat.pi / 2) | |
context.scaleBy(x: 1.0, y: -1.0) | |
let framesetter = CTFramesetterCreateWithAttributedString(attributedText) | |
let path = CGPath(rect: CGRect(x: 0.0, y: 0.0, width: rect.height, height: rect.width), transform: nil) | |
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil) | |
CTFrameDraw(frame, context) | |
} | |
} |