【问题标题】:Pixel-Position of Cursor in UITextViewUITextView 中光标的像素位置
【发布时间】:2011-02-07 16:04:01
【问题描述】:

有没有办法在 UITextView 中获取光标(闪烁条)的位置(CGPoint)(相对于其内容而言更可取)。我不是指作为 NSRange 的位置。我需要一些东西:

- (CGPoint)cursorPosition;

应该是非私有的API方式。

【问题讨论】:

  • 你说的是文本插入点吗?即闪烁的竖线?
  • 没有获取该信息的公共方法。你想达到什么目的?可能还有其他方法。
  • 是的,我就是这个意思。

标签: iphone objective-c uitextview


【解决方案1】:

需要 iOS 5

CGPoint cursorPosition = [textview caretRectForPosition:textview.selectedTextRange.start].origin;

记得在调用这个方法之前检查selectedTextRange 不是nil。您还应该使用selectedTextRange.empty 来检查它是光标位置而不是文本范围的开头。所以:

if (textview.selectedTextRange.empty) {
    // get cursor position and do stuff ...
}

【讨论】:

  • 这仅适用于 iOS 5。虽然 -caretRectForPosition 在 3.2 及更高版本中可用,但 UITextView 直到 iOS 5 才符合 UITextInput
【解决方案2】:

SWIFT 4 版本:

if let cursorPosition = textView.selectedTextRange?.start {
    // cursorPosition is a UITextPosition object describing position in the text (text-wise description)

    let caretPositionRectangle: CGRect = textView.caretRect(for: cursorPosition)
    // now use either the whole rectangle, or its origin (caretPositionRectangle.origin)
}

textView.selectedTextRange?.start 返回光标的文本位置,然后我们只需使用textView.caretRect(for:) 获取其在textView 中的像素位置。

【讨论】:

    【解决方案3】:

    这很痛苦,但您可以使用 UIStringDrawing 添加到 NSString 来做到这一点。这是我使用的通用算法:

    CGPoint origin = textView.frame.origin;
    NSString* head = [textView.text substringToIndex:textView.selectedRange.location];
    CGSize initialSize = [head sizeWithFont:textView.font constrainedToSize:textView.contentSize];
    NSUInteger startOfLine = [head length];
    while (startOfLine > 0) {
        /*
         * 1. Adjust startOfLine to the beginning of the first word before startOfLine
         * 2. Check if drawing the substring of head up to startOfLine causes a reduction in height compared to initialSize.
         * 3. If so, then you've identified the start of the line containing the cursor, otherwise keep going.
         */
    }
    NSString* tail = [head substringFromIndex:startOfLine];
    CGSize lineSize = [tail sizeWithFont:textView.font forWidth:textView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
    CGPoint cursor = origin;
    cursor.x += lineSize.width;
    cursor.y += initialSize.height - lineSize.height;
    return cursor;
    }
    

    我使用[NSCharacterSet whitespaceAndNewlineCharacterSet] 来查找单词边界。

    这也可以在CoreText 中使用CTFrameSetter 来完成(可能更有效),但这在iPhone OS 3.1.3 中不可用,所以如果你的目标是iPhone,你需要坚持使用@987654327 @。

    【讨论】:

    • 这是一个很好的解决方法,我也玩过。为了找到单词边界,我发现最好使用 CFStringTokenizer。您的实现没有给出 x 和 y 坐标,而是给出包含插入符号的行中字符的大小,直到插入符号,对吗?但是,我接受你的方法作为答案,因为我最终做了类似的事情来满足我的需求。
    • 感谢有关 CFStringTokenizer 的指针,我忽略了这一点。从 initialSize.height 中减去 lineSize.height 将为您提供光标的 y 偏移量(相对于 UITextView),最后一行片段的宽度为您提供 x 偏移量。
    • 您能否详细说明 /* * 1. 将 startOfLine 调整到 startOfLine 之前的第一个单词的开头 * 2. 检查将 head 的子字符串绘制到 startOfLine 是否会导致与 initialSize 相比高度减小. * 3. 如果是这样,那么你已经确定了包含光标的行的开头,否则继续。 */ 我还需要 UITextView 中的光标位置。
    • @tek3: initialSize 是包含整个字符串的总矩形,但它不会告诉你光标在最后一行的 x 坐标。通过从末尾切掉单词并调整逐渐变小的字符串的大小,您可以找到最后一行开头的索引。然后,使用它,您可以找到直到光标的最后一行的宽度(lineSize)。 (顺便说一句,这是一个非常好的答案。)
    • 我想,说实话 tek3 只是想知道在while 函数中放入什么代码。
    【解决方案4】:

    是的——因为有一种方法可以获取光标位置。只需使用

    CGRect caretRect = [textView rectContainingCaretSelection];
    return caretRect.origin;
    

    不——因为这个方法是私有的。没有用于此的公共 API。

    【讨论】:

    • 我搜索的正是这种方法,但我当然不能使用私有 API。其他解决方案?
    • 在 iOS 4.2 中似乎不存在。我的 UITextView 没有出现这样的选择器异常。
    【解决方案5】:

    我尝试标记选定的文本,即我收到一个 NSRange 并想在该文本后面绘制一个黄色矩形。还有其他方法吗?

    我可以告诉你一些技巧:

    NSRange selectedRange = myTextView.selectedRange;
    [myTextView select:self];
    UIMenuController* sharedMenu = [UIMenuController sharedMenuController];
    CGRect menuFrame = [sharedMenu menuFrame];
    [sharedMenu setMenuVisible:NO];
    myTextView.selectedRange = selectedRange
    

    使用此代码,您可以知道剪切/复制/过去菜单的位置,并在那里放置黄色矩形。

    我没有找到一种方法来获取菜单位置,而不会通过模拟选择操作强制它出现。

    问候 阿萨亚格

    【讨论】:

    • 我实现了这个来进行测试,这是一个很好的技巧,但实际上并没有用,因为你无法从 menuFrame 到光标位置(箭头指向下方而不是向上等问题)。跨度>
    【解决方案6】:

    截取 UITextView 的屏幕截图,然后在像素数据中搜索与光标颜色匹配的颜色。

    -(CGPoint)positionOfCursorForTextView:(UITextView)textView {
         //get CGImage from textView
         UIGraphicsBeginImageContext(textView.bounds.size);
         [textView.layer renderInContext:UIGraphicsGetCurrentContext()];
         CGImageRef textImageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage;
         UIGraphicsEndImageContext();  
    
         //get raw pixel data
         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
         uint8_t * textBuffer = (uint8_t*)malloc(Width * Height * 4);
         NSUInteger bytesPerRow = 4 * Width;
         NSUInteger bitsPerComponent = 8;
         CGContextRef context = CGBitmapContextCreate(textBuffer, Width, Height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    
    
         CGColorSpaceRelease(colorSpace);
    
         CGContextDrawImage(context, CGRectMake(0, 0, Width, Height), textImageRef);
         CGContextRelease(context);
    
         //search 
    
         for(int y = 0; y < Height; y++)
         {
             for(int x = 0; x < Width * 4; x += 4)
             {
                 int red   = textBuffer[y * 4 * (NSInteger)Width + x];
                 int green = textBuffer[y * 4 * (NSInteger)Width + x + 1];    
                 int blue  = textBuffer[y * 4 * (NSInteger)Width + x + 2];  
                 int alpha   = textBuffer[y * 4 * (NSInteger)Width + x + 3];    
    
    
                 if(COLOR IS CLOSE TO COLOR OF CURSOR)
                 {
                     free(textBuffer);
                     CGImageRelease(textImageRef);
                     return CGPointMake(x/4, y);
                 }
             }
         }
    
         free(textBuffer);
         CGImageRelease(textImageRef);
         return CGPointZero;
    }
    

    【讨论】:

    • 有创意的答案是可以的,因为容易出错和计算密集型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 2023-03-14
    • 1970-01-01
    • 2016-08-28
    相关资源
    最近更新 更多