Używanie CoreText i dotknięć do tworzenia klikalnych akcji

Mam trochę kodu w widoku, który rysuje przypisany tekst za pomocą CoreText. W tym Szukam adresów URL i robię je niebieskimi. Chodzi o to, aby nie przynosić wszystkich kosztów z UIWebView tylko po to, aby uzyskać klikalne linki. Gdy użytkownik dotknie tego łącza (nie całej komórki widoku tabeli), chcę odpalić metodę delegata, która zostanie następnie użyta do przedstawienia widoku modalnego, który zawiera widok internetowy przechodzący do tego adresu url.

Zapisuję ścieżkę i sam łańcuch jako instancję zmienne widoku, a kod rysowania dzieje się w -drawRect: (pominąłem to dla zwięzłości).

[6]}mój Touch handler, choć niekompletny, nie drukuje tego, czego bym się spodziewał. Poniżej:
- (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
                }
            }
        }
    }
}

To nie jest najładniejszy kod w tej chwili, nadal staram się go po prostu uruchomić, więc wybacz jakość kodu.

Problem, który mam polega na tym, że kiedy stuknę poza link, to czego oczekuję, stanie się: nic nie zostanie zwolnione.

Jednak spodziewałbym się "Tapped line" aby wydrukować, jeśli dotknę tej samej linii, link jest włączony, co się nie zdarza, i oczekuję, że zarówno "Tapped line", jak i "Tapped run" zostaną wydrukowane, jeśli dotknę adresu URL.

Nie jestem pewien, gdzie pójść dalej, zasoby, na które spojrzałem w celu rozwiązania tego problemu, są specyficzne dla kakao (co jest prawie całkowicie niestosowne), lub brakuje informacji na ten konkretny przypadek.

Chętnie wezmę wskazówki do dokumentacji, która szczegółowo opisuje, jak prawidłowo przejść do wykrywania, jeśli dotyk wystąpił w granicach podstawowego rysowania tekstu nad kodem, ale w tym momencie chcę tylko rozwiązać ten problem, więc każda pomoc będzie bardzo mile widziana.

UPDATE : zawęziłem swój problem do kwestii współrzędnych. Odwróciłem współrzędne (a nie jak pokazano powyżej) i problem, który dostaję jest to, że dotyka rejestru, jak bym się spodziewał, ale przestrzeń współrzędnych jest odwrócona, i nie mogę go odwrócić.

Author: jer, 2010-09-27

3 answers

Zrobiłem to, aby uzyskać indeks znaków łańcuchowych z pozycji dotyku. Numer linii to w tym przypadku i:

- (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);
}
 9
Author: Nick H247,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-12 18:32:59

Możesz spróbować dodać swój rysunek tekstowy do Kalwarii, dodać nową warstwę jako podwarstwę swojej warstwy widoków i uderzyć ją w dotyk? Jeśli to zrobisz, powinieneś być w stanie utworzyć większy dotykowy obszar, czyniąc warstwę większą niż narysowany tekst.

// hittesting 
UITouch *touch = [[event allTouches] anyObject];
touchedLayer = (CALayer *)[self.layer hitTest:[touch locationInView:self]];
 0
Author: msg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-27 12:07:38

Swift 3 wersja Nicka H247 odpowiedzi:

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
Author: KristopherGBaker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-06 02:36:55