9
votes

Utilisation de CareText et touche pour créer une action cliquable

J'ai du code dans une vue qui attire un texte attribué à l'aide de CareText. Dans cela, je cherche des URL et je leur faisons bleu. L'idée est de ne pas apporter à toutes les frais générales d'un uiwebview code> juste pour obtenir des liens cliquables. Une fois qu'un utilisateur tourne sur ce lien (pas la cellule de vue de la table entière), je souhaite déclencher une méthode de déléguée qui sera ensuite utilisée pour présenter une vue modale contenant une vue Web allant à cette URL.

Je suis Enregistrement du chemin et de la chaîne elle-même sous forme de variables d'instance de la vue et le code de dessin se produit dans -Drawrect: code> (je l'ai laissé de côté pour la brièveté). P>

My Touch Toutefois, tout en incomplète, n'imprime pas ce que je m'attendais. Il est ci-dessous: P>

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    CGContextRef context = UIGraphicsGetCurrentContext();

    NSLog(@"attribString = %@", self.attribString);
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attribString);
    CTFrameRef ctframe = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.attribString.length), attribPath, NULL);

    CFArrayRef lines = CTFrameGetLines(ctframe);
    for(CFIndex i = 0; i < CFArrayGetCount(lines); i++)
    {
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        CGRect lineBounds = CTLineGetImageBounds(line, context);

        // Check if tap was on our line
        if(CGRectContainsPoint(lineBounds, point))
        {
            NSLog(@"Tapped line");
            CFArrayRef runs = CTLineGetGlyphRuns(line);
            for(CFIndex j = 0; j < CFArrayGetCount(runs); j++)
            {
                CTRunRef run = CFArrayGetValueAtIndex(runs, j);
                CFRange urlStringRange = CTRunGetStringRange(run); 
                CGRect runBounds = CTRunGetImageBounds(run, context, urlStringRange);

                if(CGRectContainsPoint(runBounds, point))
                {
                    NSLog(@"Tapped run");
                    CFIndex* buffer = malloc(sizeof(CFIndex) * urlStringRange.length);
                    CTRunGetStringIndices(run, urlStringRange, buffer);
                    // TODO: Map the glyph indices to indexes in the string & Fire the delegate
                }
            }
        }
    }
}


3 commentaires

Avez-vous pris en compte le fait que CareText utilise un système de coordonnées renversé? À l'heure actuelle, cela me semble que vous comparez les choses dans deux systèmes de coordonnées différents.


En outre, c'est une mauvaise idée de recréer le cadrage de chaque touche comme ça. La création d'un cadresetter est très coûteuse, vous devez donc le mettre en cache lors de la première dessin ou de la définition du texte.


Ma méthodologie de développement est simplement la suivante: 1) le faire fonctionner; 2) faire le bien; 3) Rendez-le rapide / propre. Traitons le n ° 2 lorsque nous obtenons # 1 allant :) également concernant le système de coordonnées, oui, je me rends compte que, pendant un moment, je ne manipulais pas. Dans le code maintenant, après consultation d'un collègue, il me fixait tout droit, et c'est au moins détecter des robinets sur les lignes maintenant, tout simplement pas les courses. Essayant toujours de comprendre celui-là.


3 Réponses :


0
votes

Vous pouvez essayer d'ajouter votre texte de dessin dans une calayeuse, ajoutez ce nouveau calque sous forme de sous-couche de votre couche de vues et de latestest contre celui-ci dans votre touché? Si vous le faites, vous devriez pouvoir créer une zone touchable plus grande en rendant le calque plus grand que le texte dessiné.

// hittesting 
UITouch *touch = [[event allTouches] anyObject];
touchedLayer = (CALayer *)[self.layer hitTest:[touch locationInView:self]];


4 commentaires

Ensuite, j'aurais un autre problème d'avoir à diviser mon texte en deux couches différentes - celle que je veux déclencher le délégué, puis le reste. Le problème que je rencontrais, c'est ce qui se passe si l'URL est au milieu d'une ligne et il y a du texte avant et après? Ensuite, je devrais me séparer de nombreuses couches différentes. Cela pourrait devenir velu s'il y a quelques URL dans le groupe de texte que je rends. Je préférerais simplement aller chercher une liste de tous les éléments de la chaîne Attrib, j'ai coloré de couleur bleue, et obtenez-en leurs caissons de sélection, voir si l'utilisateur tapa à l'intérieur et appelez le délégué avec cette URL.


Vrai, cela pourrait être un problème. Peut-être que je n'ai pas compris votre problème. Que diriez-vous de garder le texte tel qu'il est et de superposer une couche hittable pour attraper ces touches?


J'ai toujours besoin de savoir exactement où le placer - ce qui signifie que je dois encore obtenir le rectt de l'URL. Cela me ramène à la case la case. :)


Mon problème est que j'ai du texte, je sais où dans ce texte, le lien est, mais après que je dessine, j'ai besoin de trouver ce lien. J'ai commencé juste à essayer de trouver n'importe quel point de glyphes , mais rien ne tire au point où mon NSLog () est montrant que j'ai frappé une ligne ou frappe une course de GLPHS. Sans être capable de reconnaître que j'ai frappé du texte, je n'ai aucun espoir de trouver si le texte que j'ai frappé est l'URL.



9
votes

Je viens de le faire pour obtenir l'index de caractères de chaîne de la position tactile. Le numéro de ligne serait I dans ce cas:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touch ended");

    UITouch* touch = [touches anyObject];

    CGPoint pnt = [touch locationInView:self];

    CGPoint reversePoint = CGPointMake(pnt.x, self.frame.size.height-pnt.y);

    CFArrayRef lines = CTFrameGetLines(ctFrame);

    CGPoint* lineOrigins = malloc(sizeof(CGPoint)*CFArrayGetCount(lines));

    CTFrameGetLineOrigins(ctFrame, CFRangeMake(0,0), lineOrigins);

    for(CFIndex i = 0; i < CFArrayGetCount(lines); i++)
    {
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);

        CGPoint origin = lineOrigins[i];
        if (reversePoint.y > origin.y) {
            NSInteger index = CTLineGetStringIndexForPosition(line, reversePoint);
            NSLog(@"index %d", index);
            break;
        }
    }
    free(lineOrigins);
}


6 commentaires

Merci Nick tu m'as sauvé! :)


Cela fonctionne génial, je peux comparer cela à un ensemble de gammes que j'ai stockées pour des mots ambulables. C'est beaucoup plus rapide que ma vieille approche - merci :)


Comment puis-je obtenir CTFRAME?


C'est l'objet CTFRAMERREF du cadre de texte principal actuellement en vue. Dans ma mise en œuvre, il s'agit d'une variable de membre créée avec CTFResResettercreateframe


Merci beaucoup. Cela fonctionne, mais je n'ai pas compris cela pour laquelle nous devons créer un point de version de fichier CGPoint et utiliser ce point pour la touche detétech. Quelle est la formule pour calculer le point de conversation. Pouvez-vous l'expliquer pour moi dans plus de détails.


Bonjour, oui - dans le texte de base (comme dans OSX Dev) La position de la hauteur est inversée, de sorte que le coin inférieur gauche est 0,0 - alors que dans Uikit Top gauche est 0,0!



0
votes

SWIFT 3 Version de Nick H247's Réponse:

var ctFrame: CTFrame?

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first, let ctFrame = self.ctFrame else { return }

    let pnt = touch.location(in: self.view)
    let reversePoint = CGPoint(x: pnt.x, y: self.frame.height - pnt.y)
    let lines = CTFrameGetLines(ctFrame) as [AnyObject] as! [CTLine]

    var lineOrigins = [CGPoint](repeating: .zero, count: lines.count)
    CTFrameGetLineOrigins(ctFrame, CFRange(location: 0, length: 0), &lineOrigins)

    for (i, line) in lines.enumerated() {
        let origin = lineOrigins[i]

        if reversePoint.y > origin.y {
            let index = CTLineGetStringIndexForPosition(line, reversePoint)
            print("index \(index)")
            break
        }
    }
}


0 commentaires