CodeBender
CodeBender

Reputation: 36670

Unable to render an NSAttributedString as a 2 column tabbed bullet list in a PDF

I am constructing a large string that is output into a PDF file, but right now, I'd like to have a 2 column, bulleted list in my document. However, I have yet to figure out the correct settings that will allow me to get the desired tabbing effect.

Currently, I am testing the following code:

let mutableString = NSMutableAttributedString()
let words = ["this", "is", "really", "getting", "old"]

let paragraphStyle = NSMutableParagraphStyle()
var tabStops = [NSTextTab]()
let tabInterval: CGFloat = 250.0
for index in 0..<12 {
    tabStops.append(NSTextTab(textAlignment: .left,
                              location: tabInterval * CGFloat(index),
                              options: [:]))
}
paragraphStyle.tabStops = tabStops

for index in 0..<words.count {
    if index != 0 && index % 2 == 0 {
        mutableString.append(NSAttributedString(string: "\n"))
    }
    if index % 2 == 1 {
        let attributedText = NSAttributedString(string: "\t", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
        mutableString.append(attributedText)
    }
    let word = words[index]
    let attributedString = NSMutableAttributedString(string: "\u{2022}  \(word)",
        attributes: [:])
    mutableString.append(attributedString)
}

When I feed this into my PDF generator, it produces the following result:

enter image description here

Ultimately, I want "is" and "getting" to be aligned with the middle of the document, so that I can accommodate much larger words.

Upvotes: 0

Views: 547

Answers (1)

CodeBender
CodeBender

Reputation: 36670

It turns out that I was in the ballpark, but definitely not close.

The following provides the desired split column effect:

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [
    // 274 would be the midpoint of my document
    NSTextTab(textAlignment: .left, location: 274, options: [:])
]

let string = "\u{2022} This\t\u{2022} is\n\u{2022} getting\t\u{2022} really\n\u{2022} old"

let attributedString = NSAttributedString(
    string: string,
    attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]
)

enter image description here

For bonus points, should you want to have multiple columns in your document, the following will accomplish this (pardon my crude formatting):

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [
    NSTextTab(textAlignment: .left, location: 100, options: [:]),
    NSTextTab(textAlignment: .left, location: 300, options: [:])
]

let string = "\u{2022} This\t\u{2022} is\t\u{2022} getting\n\u{2022} really\t\u{2022} old"

let attributedString = NSAttributedString(
    string: string,
    attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]
)

And will look like this:

enter image description here

What is going on here?

So, what I learned here is that the tabStops tells iOS what location within the line to place the tab:

  1. The first tab will go to position 100
  2. The second tab will go to position 300
  3. A third tab will wrap around the document and go to position 100 as well

Regarding tabbing, if you assign a tab with location 0 in the first index, then tabbing to a newline will get it aligned with the left edge.

As to what fixed the issue for me. I was relying on an approach where each component of the string was added as it was encountered. However, this string would fail to format properly. Instead, by merging everything into a single string and applying the attributes seen in my working code, I was able to get it to align properly.

I also tested using the individual components as seen in my question, but with the paragraph style attributes applied as well, and that resulted in a working solution as well.

Based on this, it appears that my mistake was to mix strings that had, and did not have, the desired tabbing behavior.

Upvotes: 0

Related Questions