2010-04-13 20 views
21

Czy istnieje sposób na uzyskanie pozycji (CGPoint) kursora (migający pasek) w UITextView (najlepiej w stosunku do jego zawartości). Nie mam na myśli położenia jako NSRange. Potrzebuję czegoś w pobliżu:Pozycja pikseli kursora w UITextView

- (CGPoint)cursorPosition; 

Powinien to być sposób API nieprywatny.

+0

Czy mówisz o punkcie wstawiania tekstu? ja.e migający pionowy pasek? –

+0

Tak, właśnie o to mi chodzi. –

+0

Nie istnieje publiczna metoda uzyskania tych informacji. Co próbujesz osiągnąć? Może być inny sposób. –

Odpowiedz

10

To bolesne, ale można użyć dodatków UIStringDrawing do NSString, aby to zrobić. Oto ogólny algorytm użyłem:

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; 
} 

użyłem [NSCharacterSet whitespaceAndNewlineCharacterSet] znaleźć granice słów.

Można to również zrobić (prawdopodobnie bardziej efektywnie) przy użyciu CTFrameSetter w CoreText, ale to nie jest dostępne w iPhone OS 3.1.3, więc jeśli jesteś kierowania iPhone trzeba będzie trzymać się UIStringDrawing.

+1

To miłe obejście, z którym również się bawiłem. Aby znaleźć granice słów, najlepiej jest użyć CFStringTokenizer. Twoja implementacja nie podaje współrzędnych X i Y, ale raczej wielkości znaków w linii zawierającej linię pomocniczą, aż do kreski, prawda? Jednak akceptuję twoje podejście jako odpowiedź, ponieważ w końcu zrobiłem coś podobnego, dostosowanego do moich potrzeb. –

+0

Dzięki za wskaźnik o CFStringTokenizer, przeoczyłem to. Odjęcie lineSize.height od initialSize.height da Ci przesunięcie y kursora (względem UITextView), a szerokość ostatniego fragmentu linii daje przesunięcie x. – Tony

+0

Czy możesz opracować więcej na temat /* * 1. Dostosuj startOfLine do początku pierwszego słowa przed startOfLine * 2. Sprawdź, czy przeciągnięcie podciągu w górę do startOfLine powoduje zmniejszenie wysokości w porównaniu do rozmiaru początkowego. * 3. Jeśli tak, zidentyfikuj początek linii zawierającej kursor, w przeciwnym razie kontynuuj. */ Wymagam także pozycji kursora w UITextView. – tek3

3

Tak - tak jak w przypadku metody uzyskiwania pozycji kursora. Po prostu używaj:

Nie - tak jak w tej metodzie jest prywatna. Nie ma publicznego interfejsu API.

+0

Dokładnie ta metoda byłaby tym, czego szukam, ale oczywiście nie mogę korzystać z prywatnego API. Inne rozwiązanie? –

+2

Wygląda na to, że nie istnieje w systemie iOS 4.2. Otrzymuję brak takiego wyjątku selektora z mojego UITextView. – mxcl

1

Próbuję zaznaczyć zaznaczony tekst, tj. Otrzymuję NSRange i chcę narysować żółty prostokąt za tym tekstem. Czy istnieje inny sposób?

mogę doradzić jakiś trick:

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

Za pomocą tego kodu, można wiedzieć, uzyskać pozycję menu cięcie/kopia/przeszłości i tam umieścić żółty prostokąt.

Nie znalazłem sposobu na zmianę pozycji menu, zmuszając go do pojawienia się podczas symulowanej operacji wyboru.

Pozdrowienia Assayag

+0

Zaimplementowałem to do testowania i jest to fajny hack, ale w rzeczywistości nie jest użyteczne, ponieważ nie można zawrzeć z menuFrame do pozycji kursora (problemy, gdy strzałka jest skierowana w dół, a nie w górę itp.). –

1

Zrób zrzut ekranu z UITextView, następnie wyszukać dane pikseli dla kolorów, które pasują do koloru kursora.

-(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 za kreatywną odpowiedź, nie na błędy podatne i intensywne obliczenia. –

+0

czy możesz mi powiedzieć, jaka jest szerokość i wysokość twojego kodu ??? –

+1

wow !!!!! to naprawdę coś innego, niż odpowiedź na to pytanie. – Tirth

46

Wymaga iOS 5

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

Pamiętaj, aby sprawdzić, czy selectedTextRange nie nil jest przed wywołaniem tej metody. Powinieneś również użyć selectedTextRange.empty, aby sprawdzić, czy jest to pozycja kursora, a nie początek zakresu tekstu. Więc:

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

To tylko iOS 5. Chociaż -caretRectForPosition jest dostępny w wersji 3.2 i nowszych, UITextView nie był zgodny z UITextInput, dopóki iOS 5 –

+0

. Dzięki Jonathan, zaktualizowałem odpowiedź. – Craz

1

SWIFT 4 wersja:

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 zwraca pozycję kursora tekstowego, a my po prostu użyć textView.caretRect(for:) dostać swoją pozycję piksela w textView.

Powiązane problemy