【问题标题】:How to find a pixel-positon of a cursor in UITextView?如何在 UITextView 中找到光标的像素位置?
【发布时间】:2010-10-13 05:11:19
【问题描述】:

我正在为 iPad 开发一个简单的写作应用程序。

我正在尝试计算光标在UITextView 中的像素位置。我花了几个星期来设计这个,但我还是想不通。

在 stackoverflow 中,Tony 编写了一个很好的算法来查找光标的像素位置。

Pixel-Position of Cursor in UITextView

我稍作修改就实现了这一点,它几乎可以为光标提供正确的像素位置。但是,它只适用于英文字母。

如果行尾有中文或日文字符,UITextView 将执行字符换行,而不是自动换行,即使中文字符之间没有空格。我认为当UITextView 仅执行自动换行(使用英文字母)时,Tony 的算法有效。

有没有其他方法可以在UITextView中找到光标的像素位置?

或者有没有办法确定一个特定的字符是像汉字一样跟随字符换行还是像英语那样换行?

加法:

这是我基于 Tony 算法的实现。我在横向模式下放置了一个UITextView,所以它的宽度是1024,我使用了21号的自定义字体。你应该适当地改变sizeOfContentWidthsizeOfContentLinesizeOfContentWidth小于实际宽度,sizeOfContentLine大于实际字号(行高>字号)。

对于混乱的代码和 cmets 感到抱歉!还是有一些小bug,如果在行尾输入汉字会给出错误的位置(没有自动换行)。

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

【问题讨论】:

  • 您能否分享您对 Tony's 所做的修改,因为我相信许多其他人正在寻找有关如何找到插入符号/光标的像素位置的答案。谢谢。
  • 嗨,我添加了基于 Tony 算法的实现。对 Tony 算法的一个补充是,在开始时,我将当前光标移到了单词的末尾。
  • 这个算法是基于 UITextView 只是做自动换行的假设。我认为我们需要一种不同的方法来计算光标的正确像素位置,以使其适用于任何类型的字符。一种解决方案是,我们可以使用 CoreText、UITextInput 和 UIKeyInput 从头开始​​制作全新的自定义文本视图,但我不想只为光标位置这样做。

标签: ios iphone ipad uitextview text-cursor


【解决方案1】:

如果你只针对 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

【讨论】:

    【解决方案2】:

    这可能效率很低,但是您能否采用与您发布的代码相同的基本原理并采用光标所在的文本行,并遍历每个单独的字符并执行[NSString sizeWithFont:forWidth:lineBreakMode:] 来计算每个字符的宽度,您可以将所有这些加起来作为您的 x 位置吗?只是一个想法,但可能有助于解决自动换行问题。

    【讨论】:

      猜你喜欢
      • 2011-02-07
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多