【问题标题】:Create UITextRange from NSRange从 NSRange 创建 UITextRange
【发布时间】:2012-02-25 22:57:04
【问题描述】:

我需要在文本视图中找到不同范围的像素帧。我正在使用- (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;
}

Apple 表示必须继承 UITextRangeUITextPosition 才能采用 UITextInput 协议。我不这样做,但我还是尝试了,按照文档的示例代码并将子类传递给 firstRectForRange 导致崩溃。

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

提前致谢。

【问题讨论】:

  • 你必须继承 UITextRange 才能设置它。设置UITextRange 属性的唯一方法是在子类中访问它们。它将startend 定义为属性,但可以通过引用_start_end 在内部设置它们。
  • 是的,但是我如何创建一个根本没有任何属性的 UITextPosition? 0.o 如果我也将其子类化,'firstRectForRange' 怎么知道从我的 UITextPosition 子类中使用哪个属性?
  • 那是我不知道的。我所知道的就是你必须子类来设置readonly 属性。这就是为什么这是评论而不是答案。

标签: objective-c ios5 uitextview nsrange


【解决方案1】:

Nicolas Bachschmidt 的 Swift 4 作为 UITextView 扩展使用 swifty Range<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)

【讨论】:

    【解决方案2】:

    Andrew Schreiber 回答的 Swift 4 便于复制/粘贴

    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
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      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
          }
      }
      

      【讨论】:

        【解决方案4】:

        看起来这么复杂有点可笑。 一个简单的“解决方法”是选择范围(接受 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;
        

        【讨论】:

        • 在 iOS 7 中,textview 必须是可选择的,否则 selectedTextRange: 返回 nil
        • 这太简单了……即使它不可选,它仍然适用于 iOS 7。
        • 最好让 SDK 为您完成工作。干得好!
        【解决方案5】:

        Here is explain.

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

        采用 UITextInput 协议的类必须创建自定义 UITextRange 对象,用于表示由管理的文本中的范围 班上。范围的开始和结束索引是 由 UITextPosition 对象表示。文本系统同时使用 用于交流文本布局的 UITextRange 和 UITextPosition 对象 信息。将对象用于文本范围有两个原因 而不是原始类型,例如 NSRange:

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

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

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

        例如those sources

        【讨论】:

          【解决方案6】:

          您可以使用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];
          }
          

          【讨论】:

          • 我有一点意见。这仅在 TextField 是第一响应者时才有效。否则,所有位置都将为 NULL,并且矩形的大小分别为零。如果有人提示如何在没有 TextField 成为第一响应者的情况下获得 rect,请与我们分享。
          • 太棒了!帮了我很多。 @thomas 对我来说,这是在没有 UITextView 作为第一响应者的情况下工作的(我在 UITableViewCell 中有一个只读的 UITextView)。
          • 我发现了与 Thomas 相同的问题 - 如果 textView 不是第一响应者,则 textView.beginningOfDocument 为零。由于此示例中的其他所有内容(顺便说一下效果很好)都基于 beginOfDocument,因此您的所有计算最终都会发送到 nil 对象。
          • 文本系统设计过度
          • 我遇到了类似的问题,没有获得beginningOfDocument 的值 - 在我的情况下,我将UITextView 设置为selectable = NO。需要将其更改为 YES...
          猜你喜欢
          • 2014-02-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-02
          • 1970-01-01
          • 2014-10-16
          • 2018-10-16
          相关资源
          最近更新 更多