2012-02-14 39 views
10

我的目标是在UITextView中标记所有可见拼写错误的单词。如何在UITextView中有效地查找可见词的CGRects?

效率低下的算法是使用拼写检查器查找文本中拼写错误的单词的所有范围,使用positionFromPosition:inDirection:offset等将它们转换为UITextRange对象,然后使用UITextInput方法firstRectFromRange获取图形rects。

因此所有的文字 - >拼错words-> NSRange收集 - > UITextRange收集 - >收集的CGRect - >评估的知名度,绘制可见那些

的问题是,这要求所有的文字被选中,并且所有拼错的单词都转换为图形矩形。因此,我想要走的路是以某种方式找出UITextView中底层文本的哪些部分,此时可见。

因此,对于文本可见的范围 - >拼错words-> NSRange收集 - > UITextRange收集 - >收集的CGRect - >评估的知名度,绘制可见那些

ios - how to find what is the visible range of text in UITextView?的代码可能工作,以此来约束要检查哪些文本部分,但仍要求所有文本都是测量的,我想这可能会相当昂贵。

有什么建议吗?

回答

18

由于UITextViewUIScrollView的一个子类,它的bounds属性反映了它的坐标系的可见部分。所以像这样的东西应该工作:

- (NSRange)visibleRangeOfTextView:(UITextView *)textView { 
    CGRect bounds = textView.bounds; 
    UITextPosition *start = [textView characterRangeAtPoint:bounds.origin].start; 
    UITextPosition *end = [textView characterRangeAtPoint:CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds))].end; 
    return NSMakeRange([textView offsetFromPosition:textView.beginningOfDocument toPosition:start], 
     [textView offsetFromPosition:start toPosition:end]); 
} 

这假设从上到下,从左到右的文字布局。如果你想使它适用于其他布局方向,你将不得不加倍努力。 :)

+0

尼斯。为了提高效率,通过从头开始移动长度字符来发现结局 - 从文档的开头开始一直很慢,但提前便宜。 – 2012-02-17 10:12:21

+0

当你说,“这样的事情应该工作”,你真的尝试过吗?这几乎是我实际可视文本范围的三倍。谢谢。 – 2014-08-01 15:56:43

+1

@JasonTyler [这是一个包含测试应用程序的回购。](https://github.com/mayoff/textview-visible-range)对我很好用。可能需要Xcode 6。 – 2014-08-01 16:48:34

3

罗布的答案,写在斯威夫特。我已经添加了一些安全检查。

private func visibleRangeOfTextView(textView: UITextView) -> NSRange { 
    let bounds = textView.bounds 
    let origin = CGPointMake(10,10) //Overcome the default UITextView left/top margin 
    let startCharacterRange = textView.characterRangeAtPoint(origin) 
    if startCharacterRange == nil { 
     return NSMakeRange(0,0) 
    } 
    let startPosition = textView.characterRangeAtPoint(origin)!.start 

    let endCharacterRange = textView.characterRangeAtPoint(CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds))) 
    if endCharacterRange == nil { 
     return NSMakeRange(0,0) 
    } 
    let endPosition = textView.characterRangeAtPoint(CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds)))!.end 

    let startIndex = textView.offsetFromPosition(textView.beginningOfDocument, toPosition: startPosition) 
    let endIndex = textView.offsetFromPosition(startPosition, toPosition: endPosition) 
    return NSMakeRange(startIndex, endIndex) 
} 

更新了夫特4:

private func visibleRangeOfTextView(textView: UITextView) -> NSRange { 
    let bounds = textView.bounds 
    let origin = CGPoint(x: 10, y: 10) //Overcome the default UITextView left/top margin 
    let startCharacterRange = textView.characterRange(at: origin) 
    if startCharacterRange == nil { 
     return NSRange(location: 0, length: 0) 
    } 
    let startPosition = textView.characterRange(at: origin)?.start 

    let endCharacterRange = textView.characterRange(at: CGPoint(x: bounds.maxX, y: bounds.maxY)) 
    if endCharacterRange == nil { 
     return NSRange(location: 0, length: 0) 
    } 
    let endPosition = textView.characterRange(at: CGPoint(x: bounds.maxX, y: bounds.maxY))!.end 

    let startIndex = textView.offset(from: textView.beginningOfDocument, to: startPosition!) 
    let endIndex = textView.offset(from: startPosition!, to: endPosition) 
    return NSRange(location: startIndex, length: endIndex) 
} 

实施例的使用,从一个按钮抽头称为:

@IBAction func buttonTapped(sender: AnyObject) { 
    let range = visibleRangeOfTextView(self.textView) 

    // Note: "as NSString" won't work correctly with Emoji and stuff, 
    // see also: http://stackoverflow.com/a/24045156/1085556 
    let nsText = self.textView.text as NSString 
    let text = nsText.substringWithRange(range) 
    NSLog("range: \(range), text = \(text)")   
}