2010-10-13 16 views
6

Je développe une application d'écriture simple pour iPad. J'essaye de calculer la position de pixel du curseur dans UITextView. Je passe quelques semaines à concevoir cela, mais je n'arrive toujours pas à le faire. Dans stackoverflow, Tony a écrit un bon algorithme pour trouver la position de pixel du curseur.Comment trouver une position de pixel d'un curseur dans UITextView?

Pixel-Position of Cursor in UITextView

Je mis en œuvre cela avec une petite modification, et il fonctionne presque qui lui donne la position de pixel correct du curseur. Cependant, cela ne fonctionne qu'avec les alphabets anglais.

S'il y a un caractère chinois ou japonais à la fin de la ligne, UITextView effectue l'habillage des caractères, au lieu de l'encapsulation, même s'il n'y a pas d'espace entre les caractères chinois. Je pense que l'algorithme de Tony fonctionne quand UITextView exécute seulement le mot-emballage (avec des alphabets anglais).

Existe-t-il un autre moyen de trouver la position de pixel du curseur dans UITextView?

Ou existe-t-il un moyen de déterminer si un caractère particulier suit l'habillage des caractères comme des caractères chinois ou un habillage de mots comme l'anglais?

Addition:

Voici mon implémentation basée sur l'algorithme de Tony. J'ai placé un UITextView en mode paysage, donc sa largeur est 1024, et j'ai utilisé la police personnalisée avec la taille 21. Vous devez changer sizeOfContentWidth et sizeOfContentLine de manière appropriée. sizeOfContentWidth est plus petite que la largeur réelle, et sizeOfContentLine est plus grande que la taille réelle de la police (hauteur de la ligne> taille de la police).

Désolé pour le code et les commentaires désordonnés! Il y a encore quelques petits bugs, et cela donne une mauvaise position si vous tapez des caractères chinois en fin de ligne (pas de retour à la ligne).

#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

Pourriez-vous la modification que vous avez fait à Tony, je suis sûr que beaucoup d'autres sont à la recherche d'une réponse sur la façon de trouver la position de pixel du curseur/curseur. Merci. – Joshua

+0

Salut, j'ai ajouté mon implémentation basée sur l'algorithme de Tony. Un ajout à l'algorithme de Tony est qu'au début, j'ai déplacé le curseur actuel à la fin du mot. – pnmn

+0

Cet algorithme est basé sur l'hypothèse que UITextView ne fait que l'empaquetage de mots. Je pense que nous avons besoin d'une approche différente pour calculer la bonne position de pixel du curseur pour le faire fonctionner avec n'importe quel type de personnage.Une solution est que nous pouvons créer une nouvelle vue de texte personnalisée en utilisant CoreText, UITextInput et UIKeyInput à partir d'un zéro, mais je ne veux pas le faire uniquement pour une position de curseur. – pnmn

Répondre

0

Ceci est probablement assez inefficace, mais pourriez-vous prendre le même principe de base du code que vous avez affiché et prendre la ligne de texte le curseur est sur, et la boucle à travers chaque caractère individuel et faire [NSString sizeWithFont:forWidth:lineBreakMode:] pour calculer chaque la largeur du personnage, et vous pouvez ajouter tous ceux-ci pour votre position x? Juste une idée, mais peut aider à résoudre le problème avec un emballage de mots.

2

Si vous ciblez à iOS 7 seulement vous pouvez utiliser méthode UITextView:

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

exemple Short:

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