Pracuję nad prostą aplikacją do pisania na iPada.Jak znaleźć piksel-positon kursora w UITextView?
Próbuję obliczyć pikseli pozycję kursora w UITextView
. Spędziłem kilka tygodni, aby to zaprojektować, ale wciąż nie mogłem tego wymyślić.
W stackoverflow, Tony napisał jeden dobry algorytm, aby znaleźć pikseli pozycję kursora.
Pixel-Position of Cursor in UITextView
I wdrożone to z niewielką modyfikacją, a to prawie działa, że daje poprawną piksel pozycję kursora. Jednak działa tylko z angielskimi alfabetami.
Jeśli na końcu wiersza znajduje się znak chiński lub japoński, UITextView
wykonuje owijanie znaków, zamiast zawijania tekstu, nawet jeśli nie ma spacji między chińskimi znakami. Myślę, że algorytm Tony działa, gdy UITextView
wykonuje tylko zawijanie słów (z angielskimi alfabetami).
Czy jest jakiś inny sposób na znalezienie piksel pozycję kursora w UITextView
?
Albo czy jest jakiś sposób, aby ustalić, czy dany znak następuje zawijanie znaków jak chińskich znaków lub słowo owijania jak po angielsku?
Dodawanie:
Oto moje wdrożenie oparte na algorytmie Tony'ego. Umieściłem jeden UITextView
w trybie poziomym, więc jego szerokość wynosi 1024, a ja niestandardową czcionkę o rozmiarze 21. Należy zmienić sizeOfContentWidth
i sizeOfContentLine
odpowiednio. sizeOfContentWidth
jest mniejsza niż rzeczywista szerokość, a sizeOfContentLine
jest większy niż rzeczywisty rozmiar czcionki (wysokość linii> rozmiar czcionki).
Niestety o kodzie brudny i komentarze! Wciąż są małe błędy i daje błędną pozycję, jeśli wpiszesz chińskie znaki na końcu linii (bez zawijania słów).
#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);
mógłbyś podzielić się zmianę wprowadzoną do Tony'ego, ponieważ jestem pewien, że wielu innych szukają odpowiedzi na pytanie, jak znaleźć pozycję pikselową karetki/kursora. Dzięki. – Joshua
Witam, dodałem swoją implementację w oparciu o algorytm Tony'ego. Jednym dodatkiem do algorytmu Tony'ego jest to, że na początku przeniosłem bieżący kursor na koniec słowa. – pnmn
Algorytm ten opiera się na założeniu, że UITextView wykonuje tylko zawijanie wierszy. Myślę, że potrzebujemy innego podejścia, aby obliczyć odpowiednią pozycję kursora w pikselach, aby działało z jakąkolwiek postacią.Jednym z rozwiązań jest to, że możemy stworzyć całkowicie nowy niestandardowy widok tekstowy za pomocą CoreText, UITextInput i UIKeyInput od podstaw, ale nie chcę tego robić tylko dla pozycji kursora. – pnmn