2010-04-13 3 views
21

Есть ли способ получить позицию (CGPoint) курсора (мигающую панель) в UITextView (предпочтительнее относительно его содержимого). Я не имею в виду местоположение как NSRange. Мне нужно что-то около:Пиксельная позиция курсора в UITextView

- (CGPoint)cursorPosition; 

Это должен быть не-частный способ API.

+0

Вы говорите о точке ввода текста? я.e мигающая вертикальная полоса? –

+0

Да, вот что я имею в виду. –

+0

Существует не общедоступный метод получения этой информации. Чего вы пытаетесь достичь? Возможно, будет другой путь. –

ответ

10

Это больно, но вы можете использовать дополнения UIStringDrawing к NSString, чтобы сделать это. Вот общий алгоритм я использовал:

CGPoint origin = textView.frame.origin; 
NSString* head = [textView.text substringToIndex:textView.selectedRange.location]; 
CGSize initialSize = [head sizeWithFont:textView.font constrainedToSize:textView.contentSize]; 
NSUInteger startOfLine = [head length]; 
while (startOfLine > 0) { 
    /* 
    * 1. Adjust startOfLine to the beginning of the first word before startOfLine 
    * 2. Check if drawing the substring of head up to startOfLine causes a reduction in height compared to initialSize. 
    * 3. If so, then you've identified the start of the line containing the cursor, otherwise keep going. 
    */ 
} 
NSString* tail = [head substringFromIndex:startOfLine]; 
CGSize lineSize = [tail sizeWithFont:textView.font forWidth:textView.contentSize.width lineBreakMode:UILineBreakModeWordWrap]; 
CGPoint cursor = origin; 
cursor.x += lineSize.width; 
cursor.y += initialSize.height - lineSize.height; 
return cursor; 
} 

Я [NSCharacterSet whitespaceAndNewlineCharacterSet] найти границы слов.

Это также можно сделать (предположительно более эффективно) с использованием CTFrameSetter в CoreText, но это не доступно в iPhone OS 3.1.3, так что, если вы ориентируетесь на iPhone вам нужно будет придерживаться UIStringDrawing.

+1

Это прекрасное обходное решение, с которым я также играл. Чтобы найти границы слов, мне было лучше использовать CFStringTokenizer. Ваша реализация не дает координаты x и y, а размер символов в строке, содержащей каретку, до каретки, правильно? Однако я принимаю ваш подход в качестве ответа, так как в конце концов я сделал что-то подобное, принятое для моих нужд. –

+0

Спасибо за указатель на CFStringTokenizer, я не обратил на это внимания. Вычитание lineSize.height из initialSize.height даст вам смещение y курсора (относительно UITextView), а ширина конечного фрагмента линии даст вам смещение x. – Tony

+0

Можете ли вы подробнее рассказать о /* * 1. Отрегулируйте startOfLine до начала первого слова перед началомOfLine * 2. Проверьте, уменьшает ли высота подстроки головы до startOfLine уменьшение высоты по сравнению с initialSize. * 3. Если да, то вы определили начало строки, содержащей курсор, в противном случае продолжайте движение. */ Мне также требуется позиция курсора в UITextView. – tek3

3

Да - как есть способ получить позицию курсора. Просто используйте

CGRect caretRect = [textView rectContainingCaretSelection]; 
return caretRect.origin; 

Нет - как в этом методе является закрытым. Для этого нет публичного API.

+0

Именно этот метод будет тем, что я ищу, но я не могу использовать частный API, конечно. Другое решение? –

+2

Кажется, не существует в iOS 4.2. Я не исключаю такого исключения из моего UITextView. – mxcl

1

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

Я могу посоветовать вам некоторые хитрости:

NSRange selectedRange = myTextView.selectedRange; 
[myTextView select:self]; 
UIMenuController* sharedMenu = [UIMenuController sharedMenuController]; 
CGRect menuFrame = [sharedMenu menuFrame]; 
[sharedMenu setMenuVisible:NO]; 
myTextView.selectedRange = selectedRange 

Используя этот код, вы можете узнать, получить позицию вырезать/копировать/прошлое меню и там разместить желтый прямоугольник.

Я не нашел способ получить положение меню witout, заставляя его отображаться с помощью моделируемой операции выбора.

С уважением Assayag

+0

Я реализовал это для тестирования, и это хороший хак, но на самом деле не полезен, так как вы не можете заключить из menuFrame позицию курсора (проблемы, когда стрелка указывает вниз, а не вверх и т. Д.). –

1

Возьмите скриншот UITextView, то поиск пиксельных данных для цветов, которые соответствуют цвету курсора.

-(CGPoint)positionOfCursorForTextView:(UITextView)textView { 
    //get CGImage from textView 
    UIGraphicsBeginImageContext(textView.bounds.size); 
    [textView.layer renderInContext:UIGraphicsGetCurrentContext()]; 
    CGImageRef textImageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage; 
    UIGraphicsEndImageContext(); 

    //get raw pixel data 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    uint8_t * textBuffer = (uint8_t*)malloc(Width * Height * 4); 
    NSUInteger bytesPerRow = 4 * Width; 
    NSUInteger bitsPerComponent = 8; 
    CGContextRef context = CGBitmapContextCreate(textBuffer, Width, Height, 
              bitsPerComponent, bytesPerRow, colorSpace, 
              kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); 


    CGColorSpaceRelease(colorSpace); 

    CGContextDrawImage(context, CGRectMake(0, 0, Width, Height), textImageRef); 
    CGContextRelease(context); 

    //search 

    for(int y = 0; y < Height; y++) 
    { 
     for(int x = 0; x < Width * 4; x += 4) 
     { 
      int red = textBuffer[y * 4 * (NSInteger)Width + x]; 
      int green = textBuffer[y * 4 * (NSInteger)Width + x + 1];  
      int blue = textBuffer[y * 4 * (NSInteger)Width + x + 2]; 
      int alpha = textBuffer[y * 4 * (NSInteger)Width + x + 3];  


      if(COLOR IS CLOSE TO COLOR OF CURSOR) 
      { 
       free(textBuffer); 
       CGImageRelease(textImageRef); 
       return CGPointMake(x/4, y); 
      } 
     } 
    } 

    free(textBuffer); 
    CGImageRelease(textImageRef); 
    return CGPointZero; 
} 
+7

Yay для творческого ответа, кроме ошибок и интенсивного вычисления. –

+0

Можете ли вы рассказать мне, что такое ширина и высота вашего кода ??? –

+1

ничего себе !!!!! его действительно что-то другое, что я проголосовал за это. – Tirth

46

Требуется IOS 5

CGPoint cursorPosition = [textview caretRectForPosition:textview.selectedTextRange.start].origin; 

Не забудьте проверить, что selectedTextRange не nil перед вызовом этого метода. Вы также должны использовать selectedTextRange.empty, чтобы проверить, что это позиция курсора, а не начало текстового диапазона. Итак:

if (textview.selectedTextRange.empty) { 
    // get cursor position and do stuff ... 
} 
+3

Это только iOS 5. Хотя -caretRectForPosition доступен 3.2 и более поздние версии, UITextView не соответствует UITextInput, пока iOS 5 –

+0

Спасибо Джонатан, я обновил ответ. – Craz

1

СВИФТ 4 версия:

if let cursorPosition = textView.selectedTextRange?.start { 
    // cursorPosition is a UITextPosition object describing position in the text (text-wise description) 

    let caretPositionRectangle: CGRect = textView.caretRect(for: cursorPosition) 
    // now use either the whole rectangle, or its origin (caretPositionRectangle.origin) 
} 

textView.selectedTextRange?.start возвращает текстовую позицию курсора, а затем мы просто используем textView.caretRect(for:), чтобы получить свои позиции пикселя в textView.

Смежные вопросы