Fix cursor size for modified paragraph spacing in UITextView

403 views Asked by At

I have a custom, editable UITextView and I modified the paragraph spacing like so:

func layoutManager(_ layoutManager: NSLayoutManager, paragraphSpacingBeforeGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
    return 10
}

func layoutManager(_ layoutManager: NSLayoutManager, paragraphSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
    return 10
}

This causes the cursor being very big. I've tried to fix that by overriding the caretRect:

override func caretRect(for position: UITextPosition) -> CGRect {
    let defaulCaretRect = super.caretRect(for: position)
    return CGRect(x: defaulCaretRect.origin.x, y: defaulCaretRect.origin.y, width: defaulCaretRect.width, height: 22)
}

It works perfectly in some cases, but in other cases, the origin.y is wrong:

wrong origin.y

If I ajdust the origin.y, it breaks it in cases where it was correct. I don't know how to recognize if the origin.y needs to be fixed or not. Am I missing something?

I've seen here on SO some older answers using the following:

rect.size.height = font.pointSize - font.descender

But fore some reason, this had no effect. Any ideas?

Lastly, the bigger paragraph spacing is also causing too big selection handles:

enter image description here

Any way of resolving that as well?

1

There are 1 answers

0
Adam Bardon On

I think I've figured it out. Adjusting the caret height with font, as suggested in other questions found on SO, seems to work great. What was missing was fixing the caret's origin Y.

I thought there must be a reason why Apple gives us the UITextPosition. Using it, I was able to get the line text and paragraph text. Having these, I can check if the caret is on the first line of the paragraph, which was causing the problems.

override func caretRect(for position: UITextPosition) -> CGRect {
    var superRect = super.caretRect(for: position)
    
    guard let paragraphRange = tokenizer.rangeEnclosingPosition(position, with: .paragraph, inDirection: .storage(.backward)),
          let paragraphText = text(in: paragraphRange)else { return superRect }
    
    guard let lineRange = tokenizer.rangeEnclosingPosition(position, with: .line, inDirection: .storage(.backward)),
          let lineText = text(in: lineRange) else { return superRect }
    
    guard let font = font else { return superRect }
    
     if paragraphText.hasPrefix(lineText) {
        superRect.origin.y += 10
    }
    
    superRect.size.height = font.pointSize - font.descender
    
    return superRect
}

I have no idea how to fix the big selection handles, though.