hkatz
hkatz

Reputation: 961

Deriving the strings corresponding to each attribute run in a Swift AttributedString

I want to be able to iterate through a (value-type) AttributedString in Swift constructed from markdown and build arrays of words, a separate array for each attribute type in the text.

For example, if the markdown is "bold moreBold italicText", the constructed arrays will be

boldWords: [String] = ["bold", "moreBold"]
italicWords: [String] = ["italicText"]

The code will look something like this:

let markdown = "**bold**  **moreBold**  *italicText*"
let attributedStr = try! AttributedString(markdown: markdown)

var boldWords: [String]
var italicWords: [String]

 
for run in attributedStr.runs {
    if let intent = run.inlinePresentationIntent {
        if intent == .emphasized {
            attributedStr[run.range].backgroundColor = .red
            italicWords += word(for: run, in: attributedStr)
        }
        else if intent == .stronglyEmphasized {
        
            boldWords += word(for: run, in: attributedStr)
        }
    }
}

For each run in the attributed string, I want to derive the word in the original markdown corresponding to that run. In other words, what does the function word() look like?

func word(for run: Run, in attributedStr: AttributedString) -> String
{
    return ???
}

From another perspective, the for run in attributedString.runs {...} construct is generally used to stylize a run of characters; I want to be able to do the inverse, deriving the strings that were so stylized to start out with.

Upvotes: -1

Views: 266

Answers (1)

matt
matt

Reputation: 534987

From another perspective, the for run in attributedString.runs {...} construct is generally used to stylize a run of characters; I want to be able to do the inverse, deriving the strings that were so stylized to start out with.

Like this:

String(attributedStr.characters[run.range])

what does the function word() look like?

 func word(for run: Run, in attributedStr: AttributedString) -> String
 {
     return ???
 }

Like this:

func word(
    for run: AttributedString.Runs.Element, 
    in attributedStr: AttributedString
) -> String {
    String(attributedStr.characters[run.range])
}

Example (from Playground, using your own sample text):

let markdown = "**bold**  **moreBold**  *italicText*"
let attributedStr = try! AttributedString(markdown: markdown)

func word(
    for run: AttributedString.Runs.Element, 
    in attributedStr: AttributedString
) -> String {
    String(attributedStr.characters[run.range])
}

for run in attributedStr.runs {
    print(word(for: run, in: attributedStr))
}

Output:

bold
  
moreBold
  
italicText

Upvotes: 0

Related Questions