Использование CORETEXT и TACKS, чтобы создать кликабельное действие

StackOverflow https://stackoverflow.com/questions/3802697

Вопрос

У меня есть какой-код в виду, который рисует некоторые приписанные текст с помощью CORETEXT. В этом я ищу URL-адреса и делаю их синим. Идея состоит в том, чтобы не принести во все накладные расходы UIWebView просто чтобы получить кликабельные ссылки. После того, как пользовательские нажатия на эту ссылку (не всю таблицу View Cell), я хочу устрелить метод делегата, который затем будет использоваться для представления модального представления, который содержит представление веб-сайта, идущего к этому URL.

Я спасаю путь и саму строку как переменные экземпляра вида, и код чертежа происходит в -drawRect: (Я оставил это для краткости).

Мой сенсорный обработчик, однако, хотя неполный, не печатает то, что я ожидаю. Это ниже:

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

На данный момент это не самый красивый код, я все еще пытаюсь просто сделать его работать, поэтому простите качество кода.

Проблема у меня есть то, что когда я нажимаю за пределами ссылки, что я ожидаю, произойдет, происходит: ничего не выпускается.

Тем не менее, я бы ожидал "Tapped line" Чтобы напечатать, если я нажатую одну и ту же строку, включена ссылка, которая не происходит, и я ожидал, что оба "Tapped line" а также "Tapped run" Чтобы напечатать, если я нажимаю на URL.

Я не уверен, что откуда взять это дальше, ресурсы, на которые я посмотрел на решение по этому вопросу, являются конкретные какао (которые почти полностью неприменимы) или отсутствуют информацию об этом конкретном случае.

Я с удовольствием приму указателям к документации, которая детализирует, как правильно отправить в обнаружение, если прикосновение произошло в границах основного текста, рисующего через код, но на данный момент я просто хочу решить эту проблему, поэтому любая помощь была бы очень Ценится.

ОБНОВИТЬ: Я сузил свою проблему в проблему координат. Я перевернул координаты (и не так, как показано выше), и проблема, которую я получаю, это касается регистрации, как я ожидаю, но координатное пространство перевернута, и я не могу перевернуть его обратно.

Это было полезно?

Решение

Я только что сделал это, чтобы получить строковый индекс символов из сенсорного положения. Номер линии будет я в этом случае:

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

Другие советы

Вы можете попытаться добавить свой текст рисунок в CALAYER, добавьте этот новый слой в качестве подслоя ваших просмотров слоя и хиттуште против него в вашем затрагивании? Если вы этого сделаете, вы должны быть в состоянии создать большую трогательную область, сделав слой больше, чем нарисованный текст.

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

Swift 3 версия 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
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top