Créer UITextRange à partir de NSRange
j'ai besoin de trouver le pixel-frame pour différentes gammes dans un textview. Je suis l'aide de la - (CGRect)firstRectForRange:(UITextRange *)range;
pour le faire. Cependant je ne peux pas trouver comment créer un UITextRange
.
en gros, C'est ce que je cherche:
- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
UITextRange*range2 = [UITextRange rangeWithNSRange:range]; //DOES NOT EXIST
CGRect rect = [textView firstRectForRange:range2];
return rect;
}
Apple affirme que l'on a à la sous-classe UITextRange
et UITextPosition
afin d'adopter le UITextInput
protocole. Je ne fais pas cela, mais j'ai quand même essayé, en suivant le code d'exemple du doc et en passant la sous-classe à firstRectForRange
qui a abouti à écraser.
S'il y a une façon plus facile d'ajouter des couleurs différentes UILables
pour un textview, s'il vous plaît dites-moi. J'ai essayé D'utiliser UIWebView avec content editable
mis à vrai, mais je n'aime pas communiquer avec JS, et la coloration est la seule chose dont j'ai besoin.
Merci d'avance.
6 réponses
Vous pouvez créer une plage de texte avec la méthode textRangeFromPosition:toPosition
. Cette méthode nécessite deux positions, de sorte que vous devez calculer les positions pour le début et la fin de votre gamme. Qui est fait avec la méthode positionFromPosition:offset
, qui renvoie une position d'une autre position et un décalage de caractère.
- (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];
}
C'est un peu ridicule qui semble être si compliqué. Une simple "solution de contournement" serait de sélectionner la plage (accepte NSRange) et de lire ensuite la Return uitextrange (retourne UITextRange):
- (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView {
textView.selectedRange = range;
UITextRange *textRange = [textView selectedTextRange];
CGRect rect = [textView firstRectForRange:textRange];
return rect;
}
cela a fonctionné pour moi même si le textView n'est pas le premier intervenant.
si vous ne voulez pas que la sélection persiste, vous pouvez soit réinitialiser la portée sélectionnée:
textView.selectedRange = NSMakeRange(0, 0);
...ou sauvegarder la sélection courante et la restaurer ensuite
NSRange oldRange = textView.selectedRange;
// do something
// then check if the range is still valid and
textView.selectedRange = oldRange;
À la question titre, voici un extension Swift 2 qui crée un UITextRange à partir d'un NSRange.
le seul initialiseur pour UITextRange est une méthode d'instance sur le protocole UITextInput, donc l'extension nécessite aussi que vous passiez dans UITextInput tel que UITextField ou UITextView.
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 de la réponse D'Andrew Schreiber pour copier/coller facile
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 de la réponse de Nicolas Bachschmidt comme un UITextView
extension utilisant swifty Range<String.Index>
au lieu de 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)
}
}
exemple d'utilisation:
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)
un objet UITextRange représente une plage de caractères dans un texte conteneur; en d'autres termes, il identifie un indice de départ et une fin de l'index dans une chaîne de caractères soutenant un objet de saisie de texte.
les Classes qui adoptent le protocole UITextInput doivent créer custom Objets UITextRange pour représenter les plages dans le texte géré par classe. Les indices de début et de fin de la gamme sont représenté par UITextPosition des objets. Le texte utilise à la fois Objets UITextRange et UITextPosition pour la communication de texte-layout information. Il y a deux raisons d'utiliser les objets pour les plages de texte plutôt que des types primitifs comme NSRange:
certains documents contiennent des éléments imbriqués (par exemple, objets embarqués) et vous avez besoin de suivre à la fois la position absolue et position dans le texte visible.
le cadre WebKit, que le texte iPhone le système est basé sur, exige que les index de texte et les décalages être représentés par des objets.
si vous adoptez le protocole UITextInput, vous devez créer un Sous-classe UITextRange ainsi qu'une sous-classe uitextposition personnalisée.
Par exemple, comme dans ces sources