santi.gs
santi.gs

Reputation: 704

Get the range of a paragraph NSAttributedString

I would like to be able to get the range(s) of the paragraph(s) overlapping with the textView.selectedRange.

My goal is to change the alignment of the whole paragraph where the selection is.

I have tried the following but the ranges provided by enumerate attribute seem to all be of length 1, making my intersection check useless.

Is there a way to get a list of continuous ranges with the same paragraph style?

var alignment: NSTextAlignment

let contentRange = NSRange(location: 0, length: editor.contentLength)

editor.attributedText.enumerateAttribute(.paragraphStyle, in: contentRange, options: .longestEffectiveRangeNotRequired) { (paragraphStyle, range, _) in
    
    //Check if attribute range contains cursor
    if NSIntersectionRange(range, editor.selectedRange).length > 0 {
        print(range)
        if let paragraphStyle = paragraphStyle as? NSParagraphStyle {
            let newStyle: NSMutableParagraphStyle = paragraphStyle.mutableParagraphStyle
            newStyle.alignment = alignment
            
            editor.addAttribute(.paragraphStyle, value: newStyle, at: range)
        }
    } 
}

Many thanks

Edit

Thanks to @Larme, having no options does indeed get a continuous range: editor.attributedText.enumerateAttribute(.paragraphStyle, in: contentRange, options: []) { (paragraphStyle, range, _) in

However, this will combine consecutive paragraphs with the same paragraph style in the same range.

i.e.

  1. The user creates three paragraphs aligned to the left
  2. The user wants to change the middle one to align right
  3. They should be able to a) select any portion of the paragraph b) have the cursor at any position in the paragraph (selection length 0) to achieve the same result (affecting the whole paragraph)
  4. The current check using enumarateAttribute will return a range grouping all three paragraphs together (as they all have the same paragraphStyle) and apply the new alignment to all of them.

The enumerateAttribute doesn't really get the range of a single paragraph it will return a consecutive range for all consecutive paragraphs with the same paragraphStyle

Is there another way to get the range of the paragraph corresponding to the selectedRange?

Upvotes: 1

Views: 1529

Answers (1)

CMill4
CMill4

Reputation: 136

A bit late to the party, but hopefully this helps.

The way I approached this problem was by saving the selected range, pulling an NSString from my NSAttributedString, then calling NSString's paragraphRange. Once you have the paragraph range, then you can enumerate however you'd like!

//0: SAVE RANGE

let rangeOfNote = yourTextView.selectedRange


//1: CONVERT NSATTRIBUTEDSTRING TO NSSTRING

let composeText = yourTextView.attributedText.string as NSString


//2: CALL NSSTRING'S PARAGRAPH RANGE (for: NSRange)

let paragraphRange = composeText.paragraphRange(for: rangeOfNote)


//3: CHECK CORRECT
    
print(composeText.substring(with: paragraphRange), paragraphRange)

//4: Use range in enumerate attributes.

yourTextView.attributedText.enumerateAttributes(in: paragraphRange, ...)

Upvotes: 1

Related Questions