2010-10-13 64 views
6

我正在为iPad开发一个简单的书写应用程序。如何在UITextView中查找游标的像素位置?

我在计算UITextView中游标的像素位置。我花了几个星期的时间来设计这个,但我仍然无法想象要做到这一点。

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

Pixel-Position of Cursor in UITextView

我实现了这个带着些许的修改,它几乎工程,它给光标的正确的像素位置。但是,它只适用于英文字母。

如果在行尾有中文或日文字符,则UITextView执行字符换行而不是换行,即使中文字符之间没有空格。我认为当UITextView只执行文字换行(用英文字母表示)时,Tony的算法才起作用。

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

还是有一种方法来确定一个特定的字符是否如下字符的包装像中国的文字或文字环绕喜欢英语吗?

增加:

这是我的实现基于托尼的算法。我以横向模式放置了一个UITextView,因此其宽度为1024,我使用大小为21的自定义字体。您应该适当更改sizeOfContentWidthsizeOfContentLinesizeOfContentWidth小于实际宽度,并且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); 
+1

你能分享你对托尼的进行的修改,因为我敢肯定,许多人正在寻找如何找到光标/光标的像素位置的答案。谢谢。 – Joshua 2010-10-13 06:08:39

+0

嗨,我添加了基于Tony算法的实现。 Tony算法的一个补充是,在开始时,我将当前光标移动到单词的末尾。 – pnmn 2010-10-13 06:57:25

+0

该算法基于UITextView仅进行单词换行的假设。我认为我们需要一种不同的方法来计算光标的正确像素位置,以使其适用于任何类型的角色。一种解决方案是,我们可以从头开始使用CoreText,UITextInput和UIKeyInput来创建全新的自定义文本视图,但我不想仅为光标位置执行此操作。 – pnmn 2010-10-13 07:20:19

回答

0

这可能是相当低效的,但你可以采取通过每个个性,你已经张贴,并采取文本光标在的行的代码相同的基本原理和循环做[NSString sizeWithFont:forWidth:lineBreakMode:]计算每个角色的宽度,你可以添加所有这些为你的X位置?只是一个想法,但可能有助于解决与字换行问题。

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