Pregunta

Tengo algo de código en una vista que dibuja un texto atribuido usando CoreText. En esto, estoy en busca de direcciones URL y haciéndolas azul. La idea es no traer a toda la sobrecarga de un UIWebView sólo para hacer clic en enlaces. Una vez un par de toques de usuario en ese enlace (no toda la célula vista de tabla), quiero fuego fuera un método delegado que luego se utiliza para presentar una vista modal que contiene una vista web va a esa URL.

Estoy ahorrando la ruta y la propia cadena como variables de instancia de la vista, y el código de dibujo que sucede en -drawRect: (lo he dejado a cabo por razones de brevedad).

Mi manejador de contacto sin embargo, aunque incompleta, no está imprimiendo lo que cabe esperar que lo haga. Es a continuación:

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

No es el código más bonito en este momento, todavía estoy tratando de hacer que funcione solo, por lo que valga la calidad del código.

El problema que estoy teniendo es que cuando toco fuera del enlace, lo que espero que sucedería, sucede:. Nada es despedido

Sin embargo, yo esperaría a "Tapped line" se imprimen si tocar en la misma línea el enlace está activado, lo cual no sucede, y yo esperaría tanto "Tapped line" y "Tapped run" que se imprimen si yo toque en la URL.

No estoy seguro de a dónde ir más lejos, los recursos que he mirado para la resolución de este problema son el cacao específica (que es casi completamente inaplicables), o que carecen de la información en este caso específico.

Con mucho gusto va a tomar punteros a la documentación que se detalla cómo ir correctamente acerca de cómo detectar si un contacto se produjo dentro de los límites de un texto básico de dibujo sobre el código, pero en este momento, sólo quiero resolver este problema, por lo que cualquier ayuda sería muy apreciado.

Actualizar : He reducido mi problema a una cuestión de coordenadas. He volteado las coordenadas (y no como se muestra arriba) y el problema que estoy recibiendo es que toques se registran como yo esperaría, pero el espacio de coordenadas se da la vuelta, y me parece que no puede darle la espalda.

¿Fue útil?

Solución

Me acaban de hacer esto para obtener el índice de cadena de caracteres desde la posición de contacto. El número de línea sería i en este 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);
}

Otros consejos

Se podría tratar de añadir su texto en un dibujo CALayer, agregar esta nueva capa como subcapa de la capa de puntos de vista y hittest contra ella en su touchesEnded? Si lo hace, usted debe ser capaz de crear una zona táctil grande al hacer la capa más grande que el texto elaborado.

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

Swift versión 3 de la respuesta 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
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top