Pergunta

Tenho um código em um modo de exibição que atrai alguns atribuíram o texto usando CoreText.Neste, eu estou procurando urls e tornando-os de azul.A ideia é não trazer a sobrecarga de um UIWebView apenas para obter links clicáveis.Uma vez que um usuário toca no link (não toda a tabela, ver célula), eu gostaria de fogo fora de um delegado método que será utilizado para apresentar um modal modo de exibição que contém um modo de exibição da web para o url.

Eu estou guardando o caminho e a própria cadeia de caracteres como variáveis de instância da vista, e o código de desenho acontece em -drawRect: (Deixei de fora por questões de brevidade).

Meu toque, processador no entanto, embora incompleto, não é a impressão que eu esperava.Ele está abaixo:

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

Não é a mais bonita de código no momento, eu ainda estou tentando apenas fazer o trabalho, então perdoe a qualidade do código.

O problema que eu estou tendo é que quando eu tocar fora o link, que eu esperava que iria acontecer, acontece:Nada é acionado.

No entanto, eu seria de esperar "Tapped line" para obter impresso, se eu tocar a mesma linha o link está no, o que não acontece, e eu seria de esperar tanto "Tapped line" e "Tapped run" para obter impresso, se eu tocar no URL.

Eu não tenho certeza de onde a tomar este mais, os recursos que eu olhei para a resolução deste problema são o Cacau específico (que é quase completamente obsoleta), ou falta de informação sobre esse caso específico.

Eu vou de bom grado sugestões para outras documentações que detalhes como a forma correta de ir sobre como detectar se um toque ocorreu dentro dos limites de um núcleo de desenho de texto sobre o código, mas neste momento, eu só quero resolver esse problema, por isso qualquer ajuda seria muito apreciada.

ATUALIZAÇÃO:Eu restringiu o meu problema para uma coordenada problema.Eu ter virado as coordenadas (e não, como mostrado acima) e o problema que eu estou ficando é que toca registrar como eu esperava, mas o espaço de coordenadas é invertida, e eu não consigo lançá-lo de volta.

Foi útil?

Solução

Eu tenho só fez isso para obter a seqüência de caracteres de índice a partir da posição de toque.O número de linha seria eu nesse caso:

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

Outras dicas

Você pode tentar adicionar o texto de desenho em um CALayer, adicione esta nova camada subcamada de seus pontos de vista e a camada de hittest contra ele em seu touchesEnded?Se você fizer isso, você deve ser capaz de criar um maior palpável área, tornando a camada maior que a de desenho de texto.

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

Swift 3 versão de Nick H247 a resposta:

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
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top