Как найти пиксельную позицию курсора в UITextView?

Я разрабатываю простое приложение для письма для iPad.

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

В stackoverflow Тони написал один хороший алгоритм для определения позиции курсора в пикселях.

Положение курсора в пикселях в UITextView

Я реализовал это с небольшой модификацией, и это почти работает, так как дает правильное положение курсора в пикселях. Однако он работает только с английскими алфавитами.

Если в конце строки есть китайский или японский символ, UITextView выполняет перенос символов вместо переноса слов, даже если между китайскими символами нет пробела. Я думаю, что алгоритм Тони работает, когда UITextView выполняет только перенос слов (с английскими алфавитами).

Есть ли другой способ найти позицию курсора в пикселях в UITextView?

Или есть способ определить, следует ли конкретный символ переноса символов, как китайские иероглифы, или переноса слов, как английский?

Добавление:

Вот моя реализация, основанная на алгоритме Тони. Я поместил один UITextView в альбомный режим, так что его ширина составляет 1024, и я использовал собственный шрифт с размером 21. Вы должны изменить sizeOfContentWidth и sizeOfContentLine соответственно. sizeOfContentWidth меньше фактической ширины, а sizeOfContentLine больше фактического размера шрифта (высота строки> размер шрифта).

Извините за грязный код и комментарии! Есть еще некоторые мелкие ошибки, и он дает неправильную позицию, если вы вводите китайские символы в конце строки (без переноса слов).

#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);

person pnmn    schedule 13.10.2010    source источник
comment
Не могли бы вы поделиться модификацией, которую вы сделали для Тони, поскольку я уверен, что многие другие ищут ответ о том, как найти позицию курсора / курсора в пикселях. Спасибо.   -  person Joshua    schedule 13.10.2010
comment
Привет, я добавил свою реализацию на основе алгоритма Тони. Одним из дополнений к алгоритму Тони является то, что вначале я переместил текущий курсор в конец слова.   -  person pnmn    schedule 13.10.2010
comment
Этот алгоритм основан на предположении, что UITextView выполняет только перенос слов. Я думаю, что нам нужен другой подход к вычислению правильной позиции курсора в пикселях, чтобы он работал с любым персонажем. Одно из решений состоит в том, что мы можем создать совершенно новое пользовательское текстовое представление с помощью CoreText, UITextInput и UIKeyInput с нуля, но я не хочу делать это только для позиции курсора.   -  person pnmn    schedule 13.10.2010
comment
@ kyu .. хотите ли вы увидеть мой вопрос, пожалуйста ... stackoverflow.com/questions/4613978/   -  person R. Dewi    schedule 07.01.2011


Ответы (2)


Если вы нацелены только на IOS7, вы можете использовать метод UITextView:

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

Краткий пример:

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
person Igor Pchelko    schedule 11.11.2013

Это, вероятно, довольно неэффективно, но не могли бы вы взять тот же основной принцип кода, который вы опубликовали, взять строку текста, на которой находится курсор, и перебрать каждый отдельный символ и сделать [NSString sizeWithFont:forWidth:lineBreakMode:] для вычисления ширины каждого символа, и вы могли бы добавить все те, кто претендует на вашу позицию x? Просто идея, но может помочь решить проблему с переносом слов.

person gdavis    schedule 29.06.2011