Skip to content

Instantly share code, notes, and snippets.

@watura
Created November 6, 2024 01:45
Show Gist options
  • Select an option

  • Save watura/08b06adffd79cab32be76eb0804306c1 to your computer and use it in GitHub Desktop.

Select an option

Save watura/08b06adffd79cab32be76eb0804306c1 to your computer and use it in GitHub Desktop.
func rubyAnnotation(text: String, ruby: String) -> NSAttributedString {
let rubyAttribute: [CFString: Any] = [
kCTRubyAnnotationSizeFactorAttributeName: 0.5
]
let rubyAnnotation = CTRubyAnnotationCreateWithAttributes(
.auto,
.auto,
.before,
ruby as CFString,
rubyAttribute as CFDictionary
)
return NSAttributedString(
string: text,
attributes:
[
kCTRubyAnnotationAttributeName as NSAttributedString.Key: rubyAnnotation
]
)
}
var dummyAttributedString: NSAttributedString {
let txt = NSMutableAttributedString(string: "だれもが")
txt.append(rubyAnnotation(text: "創作", ruby: "そうさく"))
txt.append(.init(string: "をはじめ、\n"))
txt.append(rubyAnnotation(text: "続", ruby: "つづ"))
txt.append(.init(string: "けられるようにする。"))
// ルビが範囲外に表示されてしまうので行間を広げる
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.5
txt.addAttributes([
.font: UIFont.systemFont(ofSize: 16),
.paragraphStyle: paragraphStyle,
], range: .init(location: 0, length: txt.length))
return txt
}
public struct RubyCanvas: View {
public var body: some View {
Canvas { context, size in
context.scaleBy(x: 1, y: -1)
context.translateBy(x: 0, y: -size.height)
let setter = CTFramesetterCreateWithAttributedString(dummyAttributedString)
let path = CGPath(rect: CGRect(origin: .zero, size: size), transform: nil)
let ct = CTFramesetterCreateFrame(setter, CFRange(), path, nil)
context.withCGContext { cgContext in
CTFrameDraw(ct, cgContext)
}
}
}
}
#Preview {
RubyCanvas()
.padding()
}
struct RubyTextView: UIViewRepresentable {
func makeUIView(context _: Context) -> UITextView {
let view = UITextView()
view.attributedText = dummyAttributedString
view.isScrollEnabled = false
return view
}
func updateUIView(_: UITextView, context _: Context) {}
}
#Preview {
RubyTextView()
}
final class RubyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let textView = UITextView()
textView.attributedText = dummyAttributedString
textView.isScrollEnabled = true
textView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(textView)
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0),
textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0),
textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0),
])
}
}
#Preview(traits: .defaultLayout) {
RubyViewController()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment