Question

J'ai un code dans une vue qui tire un texte attribué à l'aide CoreText. En cela, je cherche urls et de les rendre bleu. L'idée est de ne pas apporter tous les frais généraux d'un UIWebView juste pour obtenir des liens cliquables. Une fois qu'un utilisateur tape sur ce lien (et non la cellule entière vue de la table), je veux déclencher une méthode de délégué qui sera ensuite utilisé pour présenter une vue modale qui contient une vue web va à cette URL.

Je sauve le chemin et la chaîne elle-même comme variables d'instance de la vue, et le code de dessin qui se passe dans -drawRect: (je l'ai laissé pour la brièveté).

Mon gestionnaire tactile cependant, alors que incomplète, n'imprime pas ce que je pense à. Il est ci-dessous:

- (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
                }
            }
        }
    }
}

Il est pas le code plus jolie en ce moment, je suis encore en train de simplement le faire fonctionner, donc pardonner à la qualité du code.

Le problème que je vais avoir est que lorsque je tape en dehors du lien, ce que je pense se passerait-il, se produit: Rien ne se tiré

.

Cependant, je me attends "Tapped line" à faire imprimer si je tape la même ligne le lien est, ce qui ne se produit pas, et je me attends à la fois "Tapped line" et "Tapped run" à faire imprimer si je tape sur l'URL.

Je ne suis pas sûr où aller plus loin, les ressources que j'ai regardé pour la résolution de cette question sont spécifiques Cocoa (qui est presque totalement inapplicable), ou des informations manquantes sur ce cas précis.

Je vais volontiers prendre des pointeurs vers des documents qui décrivent en détail comment aller bien au sujet de détecter si une touche a eu lieu dans les limites d'un dessin de texte de base sur le code, mais à ce moment, je veux juste pour résoudre ce problème, de sorte que toute aide serait grandement apprécié.

UPDATE : J'ai rétréci mon problème à une question de coordonnées. J'ai renversé les coordonnées (et non, comme indiqué ci-dessus) et le problème que je reçois est que des touches vous inscrire comme je vous attendez, mais l'espace de coordonnées est basculée, et je ne peux pas sembler le retourner en arrière.

Était-ce utile?

La solution

Je viens de faire cela pour obtenir l'index de caractère 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);
}

Autres conseils

Vous pouvez essayer d'ajouter votre dessin de texte dans un CALayer, ajoutez cette nouvelle couche comme sous-couche de votre couche de vues et hittest contre elle dans votre touchesEnded? Si vous le faites, vous devriez être en mesure de créer une plus grande surface touchable en rendant la couche plus grande que le texte élaboré.

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

Version Swift 3 de la réponse de Nick H247:

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
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top