Ich muss den Pixelrahmen für verschiedene Bereiche in einer Textansicht finden. Ich benutze die - (CGRect)firstRectForRange:(UITextRange *)range;
, um es zu tun. Ich kann jedoch nicht herausfinden, wie man tatsächlich eine UITextRange
erstellt.
Im Grunde ist es das, was ich suche:
- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
UITextRange*range2 = [UITextRange rangeWithNSRange:range]; //DOES NOT EXIST
CGRect rect = [textView firstRectForRange:range2];
return rect;
}
Apple sagt, man muss UITextRange
und UITextPosition
in die Unterklasse unterteilen, um das UITextInput
-Protokoll zu übernehmen. Ich mache das nicht, aber ich habe es trotzdem versucht, dem Beispielcode des Dokuments zu folgen und die Unterklasse an firstRectForRange
zu übergeben, was zum Absturz führte.
Wenn es eine einfachere Möglichkeit gibt, einer Textansicht andersfarbige UILables
hinzuzufügen, teilen Sie mir dies bitte mit. Ich habe versucht, UIWebView mit content editable
auf TRUE zu setzen, aber ich mag es nicht, mit JS zu kommunizieren, und das Malen ist das einzige, was ich brauche.
Danke im Voraus.
Sie können einen Textbereich mit der Methode textRangeFromPosition:toPosition
erstellen. Diese Methode erfordert zwei Positionen. Daher müssen Sie die Positionen für den Anfang und das Ende Ihres Bereichs berechnen. Dies geschieht mit der Methode positionFromPosition:offset
, die eine Position von einer anderen Position und einen Zeichenversatz zurückgibt.
- (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];
}
Es ist ein bisschen lächerlich, das scheint so kompliziert zu sein. Eine einfache "Problemumgehung" wäre, den Bereich auszuwählen (akzeptiert NSRange) und dann den ausgewählten TextRange zu lesen (gibt UITextRange zurück)
- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
textView.selectedRange = range;
UITextRange *textRange = [textView selectedTextRange];
CGRect rect = [textView firstRectForRange:textRange];
return rect;
}
Dies funktionierte für mich auch, wenn das textView nicht der erste Ansprechpartner ist.
Wenn Sie nicht möchten, dass die Auswahl bestehen bleibt, können Sie entweder den ausgewählten Bereich zurücksetzen:
textView.selectedRange = NSMakeRange(0, 0);
... oder speichern Sie die aktuelle Auswahl und stellen Sie sie anschließend wieder her
NSRange oldRange = textView.selectedRange;
// do something
// then check if the range is still valid and
textView.selectedRange = oldRange;
Zur Titelfrage gibt es hier eine Swift-2-Erweiterung, die einen UITextRange aus einem NSRange erstellt.
Der einzige Initialisierer für UITextRange ist eine Instanzmethode für das UITextInput-Protokoll. Daher müssen Sie für die Erweiterung auch UITextInput wie UITextField oder UITextView übergeben.
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 von Andrew Schreibers Antwort für einfaches Kopieren/Einfügen
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
}
}
Swift 4 von Nicolas Bachschmidts Antwort als UITextView
-Erweiterung mit swifty Range<String.Index>
anstelle von 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)
}
}
Mögliche Verwendung:
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)