54

我需要在文本视图中找到不同范围的像素帧。我正在使用它- (CGRect)firstRectForRange:(UITextRange *)range;来做到这一点。但是我不知道如何实际创建一个UITextRange.

基本上这就是我正在寻找的:

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {

    UITextRange*range2 = [UITextRange rangeWithNSRange:range]; //DOES NOT EXIST 
    CGRect rect = [textView firstRectForRange:range2];
    return rect;
}

苹果公司表示,为了采用该协议,必须进行子UITextRangeUITextPositionUITextInput。我不这样做,但我还是尝试了,按照文档的示例代码并将子类传递给firstRectForRange导致崩溃的子类。

如果有一种更简单的方法可以向文本视图添加不同的颜色UILables,请告诉我。我曾尝试使用content editable设置为 TRUE 的 UIWebView,但我不喜欢与 JS 交流,而着色是我唯一需要的。

提前致谢。

4

6 回答 6

99

您可以使用 方法创建文本范围textRangeFromPosition:toPosition。此方法需要两个位置,因此您需要计算范围开始和结束的位置。这是通过方法来完成的,该方法positionFromPosition:offset从另一个位置返回一个位置和一个字符偏移量。

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView
{
    UITextPosition *beginning = textView.beginningOfDocument;
    UITextPosition *start = [textView positionFromPosition:beginning offset:range.location];
    UITextPosition *end = [textView positionFromPosition:start offset:range.length];
    UITextRange *textRange = [textView textRangeFromPosition:start toPosition:end];
    CGRect rect = [textView firstRectForRange:textRange];
    return [textView convertRect:rect fromView:textView.textInputView];
}
于 2012-03-07T17:36:20.587 回答
17

看起来如此复杂,有点可笑。一个简单的“解决方法”是选择范围(接受 NSRange),然后读取 selectedTextRange(返回 UITextRange):

- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
    textView.selectedRange = range;
    UITextRange *textRange = [textView selectedTextRange]; 
    CGRect rect = [textView firstRectForRange:textRange];
    return rect;
}

即使 textView 不是第一响应者,这也对我有用。


如果您不希望选择持续存在,您可以重置 selectedRange:

textView.selectedRange = NSMakeRange(0, 0);

...或保存当前选择并在之后恢复

NSRange oldRange = textView.selectedRange;
// do something
// then check if the range is still valid and
textView.selectedRange = oldRange;
于 2013-10-05T20:13:30.413 回答
10

Swift 4 of Andrew Schreiber's answer for easy copy/paste

extension NSRange {
    func toTextRange(textInput:UITextInput) -> UITextRange? {
        if let rangeStart = textInput.position(from: textInput.beginningOfDocument, offset: location),
            let rangeEnd = textInput.position(from: rangeStart, offset: length) {
            return textInput.textRange(from: rangeStart, to: rangeEnd)
        }
        return nil
    }
}
于 2017-09-17T21:08:58.473 回答
6

对于标题问题,这是一个从 NSRange 创建 UITextRange的Swift 2 扩展。

UITextRange 的唯一初始化程序是 UITextInput 协议上的实例方法,因此扩展还需要您传入 UITextInput,例如UITextFieldUITextView

extension NSRange {
    func toTextRange(textInput textInput:UITextInput) -> UITextRange? {
        if let rangeStart = textInput.positionFromPosition(textInput.beginningOfDocument, offset: location),
            rangeEnd = textInput.positionFromPosition(rangeStart, offset: length) {
            return textInput.textRangeFromPosition(rangeStart, toPosition: rangeEnd)
        }
        return nil
    }
}
于 2016-05-18T19:53:19.620 回答
0

Nicolas Bachschmidt 的回答 Swift 4 作为UITextView使用 swiftyRange<String.Index>而不是 NSRange 的扩展:

extension UITextView {
    func frame(ofTextRange range: Range<String.Index>?) -> CGRect? {
        guard let range = range else { return nil }
        let length = range.upperBound.encodedOffset-range.lowerBound.encodedOffset
        guard
            let start = position(from: beginningOfDocument, offset: range.lowerBound.encodedOffset),
            let end = position(from: start, offset: length),
            let txtRange = textRange(from: start, to: end)
            else { return nil }
        let rect = self.firstRect(for: txtRange)
        return self.convert(rect, to: textInputView)
    }
}

可能的用途:

guard let rect = textView.frame(ofTextRange: text.range(of: "awesome")) else { return }
let awesomeView = UIView()
awesomeView.frame = rect.insetBy(dx: -3.0, dy: 0)
awesomeView.layer.borderColor = UIColor.black.cgColor
awesomeView.layer.borderWidth = 1.0
awesomeView.layer.cornerRadius = 3
self.view.insertSubview(awesomeView, belowSubview: textView)
于 2018-03-09T15:46:45.327 回答
-1

这里解释一下。

UITextRange 对象表示文本容器中的一系列字符;换句话说,它在支持文本输入对象的字符串中标识起始索引和结束索引。

采用 UITextInput 协议的类必须创建自定义 UITextRange 对象来表示该类管理的文本内的范围。范围的开始和结束索引由 UITextPosition 对象表示。文本系统使用 UITextRange 和 UITextPosition 对象来传递文本布局信息。将对象用于文本范围而不是原始类型(例如 NSRange)有两个原因:

某些文档包含嵌套元素(例如,HTML 标记和嵌入对象),您需要跟踪可见文本中的绝对位置和位置。

iPhone 文本系统所基于的 WebKit 框架要求文本索引和偏移量由对象表示。

如果采用 UITextInput 协议,则必须创建自定义 UITextRange 子类以及自定义 UITextPosition 子类。

例如在那些来源中

于 2014-09-24T09:28:12.770 回答