Get the NSRange for the visible text after scroll in UITextView

╄→гoц情女王★ 提交于 2019-12-12 15:12:42

问题


I'm trying to save the location of scrolled text in a UITextView so that I can return to that location upon loading the ViewController again. I have very long strings, so I want the user to be able to scroll to a specific location and then return to that location later.

I'm using the UITextView. scrollRangeToVisible function to automatically scroll the text view, but I don't know how to get the NSRange of the text that the user is seeing. Is this the best way to go about this? I tried using the setContentOffset function but that didn't seem to do anything.

Any help is appreciated. Thanks!


回答1:


I haven't tested this thoroughly but I believe the following should work. The APIs you need are documented in the UITextInput protocol, which UITextView adopts.

You first need to get the UITextPosition that corresponds to a given point inside the view. You'd then convert this value into a UTF-16 character offset. For example, here I print the visible text range (in terms of UTF-16 code units) of a textView every time the view is scrolled:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let topLeft = CGPoint(x: textView.bounds.minX, y: textView.bounds.minY)
    let bottomRight = CGPoint(x: textView.bounds.maxX, y: textView.bounds.maxY)
    guard let topLeftTextPosition = textView.closestPosition(to: topLeft),
        let bottomRightTextPosition = textView.closestPosition(to: bottomRight)
        else {
            return
    }
    let charOffset = textView.offset(from: textView.beginningOfDocument, to: topLeftTextPosition)
    let length = textView.offset(from: topLeftTextPosition, to: bottomRightTextPosition)
    let visibleRange = NSRange(location: charOffset, length: length)
    print("Visible range: \(visibleRange)")
}

In my tests, UITextView tended to count lines that were barely included in the visible range (e.g. by only one pixel), so the reported visible range tended to be one or two lines larger than what a human user would say. You may have to experiment with the exact CGPoint you pass into closesPosition(to:) to get the results you want.




回答2:


Here's a little extension on UITextView that uses its characterRange(at:) function instead. It also adds a computed property to return the currently visible text:

extension UITextView {

    private var firstVisibleCharacterPosition: UITextPosition? {
        // ⚠️ For some reason `characterRange(at:)` returns nil for points with a low y value.
        guard let scrolledPosition = characterRange(at: contentOffset)?.start else {
            return beginningOfDocument
        }
        return scrolledPosition
    }

    private var lastVisibleCharacterPosition: UITextPosition? {
        return characterRange(at: bounds.max)?.end
    }

    /// The range of the text that is currently displayed within the text view's bounds.
    var visibleTextRange: UITextRange? {

        guard
            let first = firstVisibleCharacterPosition,
            let last = lastVisibleCharacterPosition else {
                return nil
        }

        return textRange(from: first, to: last)
    }      

    /// The string that is currently displayed within the text view's bounds.
    var visibleText: String? {
        guard let visibleTextRange = visibleTextRange else {
            return nil
        }
        return text(in: visibleTextRange)
    }

}

I used these shorthand properties in the code above:

extension CGRect {

    var min: CGPoint {
        return .init(x: minX, y: minY)
    }

    var max: CGPoint {
        return .init(x: maxX, y: maxY)
    }

}


来源:https://stackoverflow.com/questions/51107371/get-the-nsrange-for-the-visible-text-after-scroll-in-uitextview

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