繁体   English   中英

在UITextView中滚动后获取可见文本的NSRange

[英]Get the NSRange for the visible text after scroll in UITextView

我正在尝试在UITextView保存滚动文本的位置,以便我可以在再次加载ViewController时返回到该位置。 我有很长的字符串,所以我希望用户能够滚动到特定位置,然后稍后返回该位置。

我正在使用UITextView scrollRangeToVisible函数自动滚动文本视图,但我不知道如何获取用户看到的文本的NSRange。 这是解决这个问题的最好方法吗? 我尝试使用setContentOffset函数,但似乎没有做任何事情。

任何帮助表示赞赏。 谢谢!

我没有彻底测试过,但我相信以下内容应该有效。 您需要的API记录在UITextInput协议中, UITextView采用该协议。

首先需要获取与UITextPosition中给定点对应的UITextPosition 然后,您将此值转换为UTF-16字符偏移量。 例如,这里我打印的可见文本范围(以UTF-16代码单元换算)的一个textView每个视图被滚动时间:

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)")
}

在我的测试中, UITextView倾向于计算几乎不包含在可见范围内的线(例如,只有一个像素),因此报告的可见范围往往比人类用户所说的大一到两行。 您可能需要尝试将传递给closesPosition(to:)的确切CGPoint以获得所需的结果。

这是UITextView上的一个小扩展,它使用了characterRange(at:)函数。 它还添加了一个计算属性来返回当前可见的文本:

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)
    }

}

我在上面的代码中使用了这些速记属性:

extension CGRect {

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

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

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM