Search code examples
iosobjective-cswiftuitextviewnsattributedstring

How to break selection by paragraphs (Medium like) in an iOS App?


How to separate paragraphs inside a UITextView into completely isolated text clusters such as when doing selection you can only select words inside that paragraph?

In this case you could only select text "You obliged. "

enter image description here

I´m experimenting with selection cancelation when outside the paragraph, doing the required maths to define paragraph scope, but no luck so far.


Solution

  • The idea is to locate extension of current paragraph from cursor position when starting to select the text. Then allow only the intersection between ranges of the paragraph and the one corresponding to the selection.


    This is the solution as of Swift 3


    class RichTextView: UITextView {...}
    
    extension RichTextView: UITextViewDelegate {
    
      func textViewDidChangeSelection(_ textView: UITextView) {
        let range = textView.selectedRange
        if range.length > 0 {
         if let maxRange = 
           textView.attributedText.getParagraphRangeContaining(cursorPosition: range.location){
              selectedRange = NSIntersectionRange(maxRange, range)
          }
        }
      }
    }
    
    extension NSAttributedString {
    
        func getParagraphRangeContaining(cursorPosition: Int) -> NSRange? {
            let cursorPosition = cursorPosition - 1
    
            let nsText = self.string as NSString
            let textRange = NSMakeRange(0, nsText.length)
    
            var resultRange : NSRange?
            nsText.enumerateSubstrings(in: textRange, options: .byParagraphs, using: {
                (substring, substringRange, _, _) in
    
                if (NSLocationInRange(cursorPosition , substringRange)) {
                    resultRange = substringRange
                    return
                }
            })
            return resultRange
        }
    }