I'm trying to support bulleted list formatting in a UITextView
in my app. I've googled around and found various answers to questions (like here) that suggest using NSParagraphStyle
. Right now to make a given line bullet-formatted, I'm prepending a bullet (Unicode 2022) plus a tab (•\t
) and applying a paragraph style to the line generated like this, given an indent level >= 1:
func makeBulletStyle(forLevel indentLevel: Int) -> NSParagraphStyle {
let indentInterval = 15;
let result = NSMutableParagraphStyle()
//Indent the first line of this paragraph for its indent level
result.firstLineHeadIndent = CGFloat(indentLevel * indentInterval)
let tab = NSTextTab(textAlignment: .left, location: CGFloat(indentInterval))
result.tabStops = [tab]
result.defaultTabInterval = CGFloat(indentInterval)
//Indent wrapped lines in this paragraph so they line up under the first line,
//which is indented and then tabbed. Basically firstLineHeadIndent + indentInterval
result.headIndent = CGFloat((indentLevel + 1) * indentInterval)
return result
}
This appears to work if I always use the same bullet character for the line prefix. But if I switch to using different characters for different levels, like most format-capable text editors do - empty circles, triangles, etc - then the indent and tab distances get wonky, and things don't line up anymore.
These two screenshots only change the tab characters:
What am I doing wrong here? Why do different tab characters cause issues? Right now I'm working under the admittedly shaky assumptions that:
- The indent properties and the tab properties on
NSParagraphStyle
are using the same units. The docs onNSTextTab.init
don't mention units, butNSTextTab
itself mentions inches.NSParagraphStyle.firstLineHeadIndent
says points.NSParagraphStyle.tabStops
mentions the default value in terms of points. - The tab distance specified with
NSTextTab
is measured from thefirstLineHeadIndent
position.
I also don't quite know what role the indent interval plays here, but if I don't set it then the text on each list item ends up on the line after the bullet itself.