UITextInput.characterRange(at:) is off by a few pixels

醉酒当歌 提交于 2019-12-11 15:13:00

问题


After adding a tap recognizer to my UITextView subclass, I'm attempting to get the character that is being tapped:

var textRecognizer: UITapGestureRecognizer!
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    textContainer.lineFragmentPadding = 0
    textContainerInset = .zero

    textRecognizer = UITapGestureRecognizer(target: self, action: #selector(textTapped))
    textRecognizer.numberOfTapsRequired = 1
    addGestureRecognizer(textRecognizer)
}

@objc func textTapped(recognizer: UITapGestureRecognizer) {
    let location = recognizer.location(in: self)
    if let cRange = characterRange(at: location) {
        let cPosition = offset(from: beginningOfDocument, to: cRange.start)
        let cChar = text[Range(NSRange(location: cPosition, length: 1), in: text)!]
        print(cChar)
    }
}

Problem is that if my attributedText is "Hello world\nWelcome to Stack Overflow" and I tap on the left part of a letter, like the left side of letter f, then characterRange(at: location) returns the previous letter r instead of returning f.


回答1:


From my perspective, characterRange(at:) is buggy:

  • If you give it a point on the left half of character at index n, it returns range (n-1, n)
  • If you give it a point on the right half of character at index n, it returns range (n, n+1)
  • If you give it a point on the left half of character at index beginningOfDocument, it returns nil
  • If you give it a point on the right half of character at index endOfDocument, it returns (endOfDocument, endOfDocument+1)

The discrepancy of the behaviors at the extremities of the textInput demonstrate that there is a bug somewhere.

It behaves like a sort of "cursor position at point" function, which makes it unreliable to determine which character is actually at this point: is it the character before the cursor or the character after the cursor?

closestPosition(to:) suffers from the exact same issue.

A working alternative is layoutManager.characterIndex(for:in:fractionOfDistanceBetweenInsertionPoints:). Credit to vacawama:

@objc func textTapped(recognizer: UITapGestureRecognizer) {
    var location = recognizer.location(in: self)
    location.x -= textContainerInset.left
    location.y -= textContainerInset.top
    let cPosition = layoutManager.characterIndex(for: location, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    let cChar = text[Range(NSRange(location: cPosition, length: 1), in: text)!]
    print(cChar)
}


来源:https://stackoverflow.com/questions/49551760/uitextinput-characterrangeat-is-off-by-a-few-pixels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!