【问题标题】:Get the NSRange for the visible text after scroll in UITextView在 UITextView 中滚动后获取可见文本的 NSRange
【发布时间】:2018-06-29 18:16:49
【问题描述】:

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

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

感谢任何帮助。谢谢!

【问题讨论】:

  • 要产生这个问题,你能分享任何代码吗?

标签: ios swift uitextview nsrange


【解决方案1】:

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

}

【讨论】:

    【解决方案2】:

    我尚未对此进行彻底测试,但我相信以下内容应该可行。您需要的 API 记录在 UITextInput 协议中,UITextView 采用了该协议。

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

    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 以获得所需的结果。

    【讨论】:

    • 谢谢,这很有帮助。我尝试实现这一点,使 CGPoint 超出 bounds.maxY 而不是使用 bounds.origin。这在文本的第一季度工作得很好,但在那之后它又开始重新开始,这很奇怪。
    • 嗯,这对我来说似乎工作正常。我编辑了答案以包含更完整的代码示例。我只在一个纯文本(无格式)的操场上测试了这个,长度约为。 3,000 个字符,但我已确认报告的值是准确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    相关资源
    最近更新 更多