2010-10-13 4 views
6

Sto sviluppando un'applicazione di scrittura semplice per iPad.Come trovare una posizione dei pixel di un cursore in UITextView?

Sto provando a calcolare la posizione dei pixel del cursore in UITextView. Trascorro un paio di settimane per progettare questo, ma non riesco ancora a capire di farlo.

In stackoverflow, Tony ha scritto un buon algoritmo per trovare la posizione dei pixel del cursore.

Pixel-Position of Cursor in UITextView

ho attuato questo con una piccola modifica, e funziona quasi che dà la corretta pixel-posizione del cursore. Tuttavia, funziona solo con alfabeti inglesi.

Se alla fine della riga è presente un carattere cinese o giapponese, lo UITextView esegue il wrapping dei caratteri, anziché il ritorno a capo, anche se non vi è spazio tra i caratteri cinesi. Penso che l'algoritmo di Tony funzioni quando UITextView esegue solo il word-wrapping (con alfabeti inglesi).

C'è un altro modo per trovare la posizione dei pixel del cursore in UITextView?

O c'è un modo per determinare se un particolare personaggio segue il carattere come caratteri cinesi o il wordwrap come l'inglese?

Aggiunta:

Ecco la mia implementazione basata su un algoritmo di Tony. Ho inserito uno UITextView in modalità orizzontale, quindi la sua larghezza è 1024 e ho utilizzato il carattere personalizzato con la dimensione 21. È necessario modificare sizeOfContentWidth e sizeOfContentLine in modo appropriato. sizeOfContentWidth è più piccola della larghezza effettiva e sizeOfContentLine è più grande della dimensione del carattere reale (altezza della linea> dimensione del carattere).

Siamo spiacenti per il codice e commenti disordinati! Ci sono ancora alcuni piccoli bug, e dà una posizione sbagliata se si digitano caratteri cinesi alla fine della riga (nessun word-wrap).

#define sizeOfContentWidth 1010 
#define sizeOfContentHeight 1000 
#define sizeOfContentLine 25 

    // Stores the original position of the cursor 
NSRange originalPosition = textView.selectedRange;  

// Computes textView's origin 
CGPoint origin = textView.frame.origin; 

// Checks whether a character right to the current cursor is a non-space character 
unichar c = ' '; 

if(textView.selectedRange.location != [textView.text length]) 
    c = [textView.text characterAtIndex:textView.selectedRange.location]; 

// If it's a non-space or newline character, then the current cursor moves to the end of that word 
if(c != 32 && c != 10){ 
    NSRange delimiter = [textView.text rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] 
                 options:NSLiteralSearch 
                 range:NSMakeRange(textView.selectedRange.location, [textView.text length] - textView.selectedRange.location)]; 

    if(delimiter.location == NSNotFound){ 
     delimiter.location = [textView.text length]; 
    } 

    textView.selectedRange = delimiter; 
} 

// Deviation between the original cursor location and moved location 
int deviationLocation = textView.selectedRange .location - originalPosition.location; 

// Substrings the part before the cursor position 
NSString* head = [textView.text substringToIndex:textView.selectedRange.location]; 

// Gets the size of this part 
CGSize initialSize = [head sizeWithFont:textView.font constrainedToSize:CGSizeMake(sizeOfContentWidth, sizeOfContentHeight)]; 

// Gets the length of the head 
NSUInteger startOfLine = [head length]; 

// The first line 
BOOL isFirstLine = NO; 

if(initialSize.height/sizeOfContentLine == 1){ 
    isFirstLine = YES; 
} 

while (startOfLine > 0 && isFirstLine == NO) { 
    // 1. Adjusts startOfLine to the beginning of the first word before startOfLine 
    NSRange delimiter = [head rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] options:NSBackwardsSearch range:NSMakeRange(0, startOfLine)]; 

    // Updates startsOfLine 
    startOfLine = delimiter.location; 

    // 2. Check if drawing the substring of head up to startOfLine causes a reduction in height compared to initialSize. 
    NSString *tempHead = [head substringToIndex:startOfLine]; 

    // Gets the size of this temp head 
    CGSize tempHeadSize = [tempHead sizeWithFont:textView.font constrainedToSize:CGSizeMake(sizeOfContentWidth, sizeOfContentHeight)]; 

    // Counts the line of the original 
    int beforeLine = initialSize.height/sizeOfContentLine; 

    // Counts the line of the one after processing 
    int afterLine = tempHeadSize.height/sizeOfContentLine; 

    // 3. If so, then you've identified the start of the line containing the cursor, otherwise keep going. 
    if(beforeLine != afterLine) 
     break; 
} 

// Substrings the part after the cursor position 
NSString* tail; 

if(isFirstLine == NO) 
    tail = [head substringFromIndex:(startOfLine + deviationLocation)]; 
else { 
    tail = [head substringToIndex:(startOfLine - deviationLocation)]; 
} 

// Gets the size of this part 
CGSize lineSize = [tail sizeWithFont:textView.font forWidth:sizeOfContentWidth lineBreakMode:UILineBreakModeWordWrap]; 

// Gets the cursor position in coordinate 
CGPoint cursor = origin;  
cursor.x += lineSize.width; 
cursor.y += initialSize.height - lineSize.height; 

// Back to the original position 
textView.selectedRange = originalPosition; 

// Debug 
printf("x: %f, y: %f\n", cursor.x, cursor.y); 
+1

Potrebbe condividere la modifica che hai fatto a Tony di, come sono sicuro che molti altri sono alla ricerca di una risposta su come trovare la posizione dei pixel del cursore di inserimento/cursore. Grazie. – Joshua

+0

Ciao, ho aggiunto la mia implementazione basata sull'algoritmo di Tony. Un'aggiunta all'algoritmo di Tony è che all'inizio ho spostato il cursore corrente alla fine della parola. – pnmn

+0

Questo algoritmo si basa sul presupposto che UITextView stia facendo solo il word-wrapping. Penso che abbiamo bisogno di un approccio diverso per calcolare la giusta posizione dei pixel del cursore per farlo funzionare con qualsiasi tipo di carattere.Una soluzione è che possiamo creare una vista del testo completamente nuova utilizzando CoreText, UITextInput e UIKeyInput partendo da zero, ma non voglio farlo solo per una posizione del cursore. – pnmn

risposta

0

Questo è probabilmente piuttosto inefficiente, ma potrebbe prendere lo stesso principio di base del codice che avete pubblicato e prendere la riga di testo il cursore è su, e ciclo attraverso ogni singolo personaggio e fare [NSString sizeWithFont:forWidth:lineBreakMode:] per calcolare ogni la larghezza del personaggio, e potresti aggiungere tutti quelli per la tua posizione x? Solo un'idea, ma può aiutare a risolvere il problema con il word wrapping.

2

Se il target a iOS7 solo è possibile utilizzare metodo UITextView:

- (CGRect)caretRectForPosition:(UITextPosition *)position; 

campione breve:

NSRange range; // target location in text that you should get from somewhere, e.g. textview.selectedRange 
UITextView textview; // the text view 

UITextPosition *start = [textview positionFromPosition:textview.beginningOfDocument offset:range.location]; 
CGRect caretRect = [self caretRectForPosition:start]; // caret rect in UITextView