This is part 2. The previous episode is here.
To quickly recap: We want to layout text that looks like this:
When I began looking into implementation options, all roads seemed to be leading toward an AttributedString in a UILabel. There are a variety of descriptions of CoreText support for something called Ruby Text. There is also a mark down schemes for expressing Ruby text and sample code showing how to tickle the belly of CoreText to layout furigana text. I was not able to get any CoreText furigana working and there are likely two reasons for this:
- There are reports (not substantiated by me) that Apple removed Ruby Text support from Core Text. This seems like an odd thing, but definitely plausible
- I really wanted to do something that was SwiftUI-ish (SwiftUI-y?)
It’s quite possible that if I’d kept hammering away at CoreText I’d have got it to work. My official excuse is my heart wasn’t in it.
To implement this in SwiftUI my first approach included:
- a markdown scheme, eg “おはよおはよざいます。<<今日((こんいち))>>は。”
- code to convert markdown strings (like the one above) into an array of model objects
struct TextModel {
let mainText: String
let furiGana: String
}
- UI code that takes in an array of TextModels and displays them in collection of VStacks in an HStack
struct TextCollection: View {
let textElements: [TextModel]
init(markdown: String) {
self.textElements = markdown.convertedToElements
}
var body: some View {
HStack() {
ForEach(textElements) { element in
VStack() {
Text(element.furiGana)
Text(element.mainText)
}
}
}
}
}
The result looks like this:
Not yet perfect, but a great start!
In the next part, I’ll discuss my journey to figure out how to size the furigana as a function of the size of the main text. Stay tuned!