2012-02-03 3 views
40

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

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

- (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, и раскраска - единственное, что мне нужно.

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

+0

У вас должен быть подкласс 'UITextRange', чтобы его можно было установить. Единственный способ установить свойства 'UITextRange' - получить доступ к ним в подклассе. Он определяет свойства 'start' и' end' как свойства, но их можно установить внутри, ссылаясь на '_start' и' _end'. – Justin

+0

Да, но как я могу создать UITextPosition, у которого вообще нет каких-либо свойств? 0.o Если я также подклассифицирую его, как «firstRectForRange» знает, какое свойство использовать из моего подкласса UITextPosition? –

+0

Это то, чего я не знаю. Все, что я знаю об этом, это то, что вы должны подклассифицировать свойства 'readonly'. Вот почему это комментарий вместо ответа. – Justin

ответ

80

Вы можете создать текстовый диапазон с помощью метода 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]; 
} 
+5

У меня есть небольшое замечание. Это работает, только если TextField является первым ответчиком. В противном случае все позиции будут NULL, а прямоугольник имеет нулевой размер соответственно. Если у кого-то есть намек на то, как можно получить прямоугольник, если TextField не будет первым ответчиком, пожалуйста, поделитесь им с нами. – thomas

+0

Спасибо большое, Николас! Это должно быть действительно документировано лучше. –

+1

Удивительный! Помог мне много. @thomas для меня это работает без того, чтобы 'UITextView' был первым ответчиком (у меня есть UITextView только для чтения внутри' UITableViewCell'). – mluisbrown

15

Это немного смешно, что кажется настолько сложным. Простой «обходной путь» должен был бы выбрать диапазон (принимает 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 не первый ответчик.


Если вы не хотите, выбор упорствовать, вы можете сбросить selectedRange:

textView.selectedRange = NSMakeRange(0, 0); 

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

NSRange oldRange = textView.selectedRange; 
// do something 
// then check if the range is still valid and 
textView.selectedRange = oldRange; 
+0

в iOS 7 текстовое изображение должно быть выбрано, в противном случае выбраноTextRange: возвращает nil – ZeCodea

+1

Это настолько смехотворно просто ... и даже если он не подбирается, он по-прежнему работает на iOS 7. – brandonscript

+0

Подтверждено. Это работает. – ervinbosenbacher

-1

Here is explain.

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

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

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

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

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

Например, как в those sources

5

к названию вопроса, здесь 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 
    } 
} 
2

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

Swift 4 ответа Николя Bachschmidt в качестве 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) 
Смежные вопросы