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.

45
demandé sur Johannes Lund 2012-02-03 13:53:47

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];
}
85
répondu Nicolas Bachschmidt 2014-06-23 22:20:22

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;
15
répondu auco 2016-05-17 10:23:12

À 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
    }
}
5
répondu Andrew Schreiber 2016-05-18 19:53:19

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
    }
}
3
répondu CodenameDuchess 2017-09-17 21:08:58

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)
0
répondu Victor 2018-03-09 15:46:45

Voici une explication.

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

-1
répondu SAKrisT 2014-09-24 09:28:12