web-dev-qa-db-ja.com

NSRangeからUITextRangeを作成します

テキストビューのさまざまな範囲のピクセルフレームを見つける必要があります。 - (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は、UITextRangeプロトコルを採用するには、UITextPositionUITextInputをサブクラス化する必要があると言います。私はそれをしませんが、とにかく、ドキュメントのサンプルコードに従ってサブクラスをfirstRectForRangeに渡し、クラッシュを引き起こしました。

異なる色のUILablesをテキストビューに追加する簡単な方法があれば、教えてください。 content editableをTRUEに設定してUIWebViewを使用しようとしましたが、JSと通信することは好きではなく、色付けだけが必要です。

前もって感謝します。

48
Johannes Lund

メソッドtextRangeFromPosition:toPositionを使用してテキスト範囲を作成できます。この方法には2つの位置が必要なので、範囲の開始位置と終了位置を計算する必要があります。これは、別の位置からの位置と文字オフセットを返すメソッド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];
}
90

とてもばかげているように見えるのは少しばかげています。簡単な「回避策」は、範囲を選択し(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;
17
auco

タイトルの質問に、NSRangeからUITextRangeを作成するSwift 2拡張があります。

UITextRangeの初期化子はUITextInputプロトコルのインスタンスメソッドのみであるため、拡張機能ではITextFieldまたはITextViewなどのUITextInputを渡す必要もあります。

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

アンドリューシュライバーの簡単なコピー/貼り付けの答えの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
    }
}
5
CodenameDuchess

NSRangeの代わりにswifty Range<String.Index>を使用したUITextView拡張としてのNicolas Bachschmidtの回答のSwift 4:

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)
0
Victor