テキストビューのさまざまな範囲のピクセルフレームを見つける必要があります。 - (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
プロトコルを採用するには、UITextPosition
とUITextInput
をサブクラス化する必要があると言います。私はそれをしませんが、とにかく、ドキュメントのサンプルコードに従ってサブクラスをfirstRectForRange
に渡し、クラッシュを引き起こしました。
異なる色のUILables
をテキストビューに追加する簡単な方法があれば、教えてください。 content editable
をTRUEに設定してUIWebViewを使用しようとしましたが、JSと通信することは好きではなく、色付けだけが必要です。
前もって感謝します。
メソッド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];
}
とてもばかげているように見えるのは少しばかげています。簡単な「回避策」は、範囲を選択し(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;
タイトルの質問に、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
}
}
アンドリューシュライバーの簡単なコピー/貼り付けの答えの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
}
}
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)