Создать UITextRange из NSRange - PullRequest
       101

Создать UITextRange из NSRange

47 голосов
/ 03 февраля 2012

Мне нужно найти пиксельную рамку для разных диапазонов в текстовом представлении.Я использую - (CGRect)firstRectForRange:(UITextRange *)range;, чтобы сделать это.Однако я не могу выяснить, как на самом деле создать UITextRange.

В основном это то, что я ищу:

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {

    UITextRange*range2 = [UITextRange rangeWithNSRange:range]; //DOES NOT EXIST 
    CGRect rect = [textView firstRectForRange:range2];
    return rect;
}

Apple говорит, что нужно подкласс UITextRangeи UITextPosition для принятия протокола UITextInput.Я этого не делаю, но я все равно попробовал, следуя коду примера документа и передав подкласс в firstRectForRange, что привело к сбою.

Если есть более простой способ добавления разноцветного UILables втекстовое представление, пожалуйста, скажите мне.Я пытался использовать UIWebView с content editable, установленным в TRUE, но я не люблю общаться с JS, и мне нужно только окрашивание.

Заранее спасибо.

Ответы [ 6 ]

89 голосов
/ 07 марта 2012

Вы можете создать текстовый диапазон с помощью метода textRangeFromPosition:toPosition. Этот метод требует двух позиций, поэтому вам нужно вычислить позиции для начала и конца вашего диапазона. Это делается с помощью метода positionFromPosition:offset, который возвращает позицию из другой позиции и смещение символа.

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView
{
    UITextPosition *beginning = textView.beginningOfDocument;
    UITextPosition *start = [textView positionFromPosition:beginning offset:range.location];
    UITextPosition *end = [textView positionFromPosition:start offset:range.length];
    UITextRange *textRange = [textView textRangeFromPosition:start toPosition:end];
    CGRect rect = [textView firstRectForRange:textRange];
    return [textView convertRect:rect fromView:textView.textInputView];
}
17 голосов
/ 06 октября 2013

Это немного смешно, что кажется таким сложным. Простой «обходной путь» - выбрать диапазон (принимает NSRange) и затем прочитать selectedTextRange (возвращает UITextRange):

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
    textView.selectedRange = range;
    UITextRange *textRange = [textView selectedTextRange]; 
    CGRect rect = [textView firstRectForRange:textRange];
    return rect;
}

Это сработало для меня, даже если textView не первый респондент.


Если вы не хотите, чтобы выделение сохранялось, вы можете сбросить выбранный диапазон:

textView.selectedRange = NSMakeRange(0, 0);

... или сохранить текущий выбор и восстановить его впоследствии

NSRange oldRange = textView.selectedRange;
// do something
// then check if the range is still valid and
textView.selectedRange = oldRange;
6 голосов
/ 18 мая 2016

К заглавному вопросу приведено расширение Swift 2 , которое создает UITextRange из NSRange.

Единственным инициализатором для UITextRange является метод экземпляра в протоколе UITextInput, таким образом,расширение также требует, чтобы вы передавали UITextInput, например UITextField или UITextView .

extension NSRange {
    func toTextRange(textInput textInput:UITextInput) -> UITextRange? {
        if let rangeStart = textInput.positionFromPosition(textInput.beginningOfDocument, offset: location),
            rangeEnd = textInput.positionFromPosition(rangeStart, offset: length) {
            return textInput.textRangeFromPosition(rangeStart, toPosition: rangeEnd)
        }
        return nil
    }
}
5 голосов
/ 18 сентября 2017

Swift 4 от Эндрю Шрайбера за легкое копирование / вставку

extension NSRange {
    func toTextRange(textInput:UITextInput) -> UITextRange? {
        if let rangeStart = textInput.position(from: textInput.beginningOfDocument, offset: location),
            let rangeEnd = textInput.position(from: rangeStart, offset: length) {
            return textInput.textRange(from: rangeStart, to: rangeEnd)
        }
        return nil
    }
}
0 голосов
/ 09 марта 2018

Swift 4 от ответа Николаса Бахшмидта как расширение UITextView с использованием swifty Range<String.Index> вместо NSRange:

extension UITextView {
    func frame(ofTextRange range: Range<String.Index>?) -> CGRect? {
        guard let range = range else { return nil }
        let length = range.upperBound.encodedOffset-range.lowerBound.encodedOffset
        guard
            let start = position(from: beginningOfDocument, offset: range.lowerBound.encodedOffset),
            let end = position(from: start, offset: length),
            let txtRange = textRange(from: start, to: end)
            else { return nil }
        let rect = self.firstRect(for: txtRange)
        return self.convert(rect, to: textInputView)
    }
}

Возможное использование:

guard let rect = textView.frame(ofTextRange: text.range(of: "awesome")) else { return }
let awesomeView = UIView()
awesomeView.frame = rect.insetBy(dx: -3.0, dy: 0)
awesomeView.layer.borderColor = UIColor.black.cgColor
awesomeView.layer.borderWidth = 1.0
awesomeView.layer.cornerRadius = 3
self.view.insertSubview(awesomeView, belowSubview: textView)
0 голосов
/ 24 сентября 2014

Вот объяснение.

Объект UITextRange представляет диапазон символов в текстовом контейнере;другими словами, он идентифицирует начальный индекс и конечный индекс в строке, поддерживающей объект ввода текста.

Классы, которые принимают протокол UITextInput, должны создавать собственные объекты UITextRange для представления диапазонов в тексте, управляемом классом.Начальный и конечный индексы диапазона представлены объектами UITextPosition.Текстовая система использует объекты UITextRange и UITextPosition для передачи информации о макете текста.Существует две причины использования объектов для текстовых диапазонов, а не примитивных типов, таких как NSRange:

Некоторые документы содержат вложенные элементы (например, теги HTML и внедренные объекты), и вам необходимо отслеживать как абсолютную позицию, так и позицию ввидимый текст.

Платформа WebKit, на которой основана текстовая система iPhone, требует, чтобы текстовые индексы и смещения были представлены объектами.

Если вы принимаете протокол UITextInput, вы должны создатьпользовательский подкласс UITextRange, а также пользовательский подкласс UITextPosition.

Например, как в эти источники

...