Получите NSRange для видимого текста после прокрутки в UITextView - PullRequest
0 голосов
/ 29 июня 2018

Я пытаюсь сохранить местоположение прокручиваемого текста в UITextView, чтобы я мог вернуться в это место после повторной загрузки ViewController. У меня очень длинные строки, поэтому я хочу, чтобы пользователь имел возможность прокрутки до определенного места, а затем позже вернулся в это место.

Я использую UITextView. Функция scrollRangeToVisible для автоматической прокрутки текстового представления, но я не знаю, как получить NSRange текста, который видит пользователь. Это лучший способ сделать это? Я пытался использовать функцию setContentOffset, но, похоже, ничего не помогло.

Любая помощь приветствуется. Спасибо!

Ответы [ 2 ]

0 голосов
/ 27 декабря 2018

Вот небольшое расширение на 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)
    }

}
0 голосов
/ 29 июня 2018

Я не проверил это полностью, но я считаю, что следующее должно работать. Необходимые API-интерфейсы документированы в протоколе UITextInput, который UITextView принимает.

Сначала необходимо получить 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 имел тенденцию подсчитывать строки, которые были едва включены в видимый диапазон (например, только на один пиксель), поэтому сообщаемый видимый диапазон имел тенденцию быть на одну или две строки больше, чем сказал бы пользователь-человек , Возможно, вам придется поэкспериментировать с точным CGPoint, который вы передаете в closesPosition(to:), чтобы получить желаемые результаты.

...