Reputation: 1614
I am using CoreText to layout a custom view. The next step for me is to detect which word is tapped on a touch event/gesture event. I have done research on this and found advise on how to custom label a url to receive touches - but nothing generic. Does any one have any idea on how to do this?
UPDATE:
Here is the code within my drawRect: method self.attribString = [aString copy];
CGContextRef context = UIGraphicsGetCurrentContext();
// Flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable(); //1
CGPathAddRect(path, NULL, self.bounds );
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)aString); //3
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [aString length]), path, NULL);
CTFrameDraw(frame, context); //4
UIGraphicsPushContext(context);
frameRef = frame;
CFRelease(path);
CFRelease(framesetter);
Here is where I am trying to handle the touch event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
CGContextRef context = UIGraphicsGetCurrentContext();
CFArrayRef lines = CTFrameGetLines(frameRef);
for(CFIndex i = 0; i < CFArrayGetCount(lines); i++)
{
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CGRect lineBounds = CTLineGetImageBounds(line, context);
NSLog(@"Line %ld (%f, %f, %f, %f)", i, lineBounds.origin.x, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height);
NSLog(@"Point (%f, %f)", point.x, point.y);
if(CGRectContainsPoint(lineBounds, point))
{
It seems that CTLineGetImageBounds is returning a wrong origin (the size seems correct) here is one example of the NSLog "Line 0 (0.562500, -0.281250, 279.837891, 17.753906)".
Upvotes: 2
Views: 1306
Reputation: 299565
There is no "current context" in touchesEnded:withEvent:
. You are not drawing at this point. So you can't call CTLineGetImageBounds()
meaningfully.
I believe the best solution here is to use CTFrameGetLineOrigins()
to find the correct line (by checking the Y origins), and then using CTLineGetStringIndexForPosition()
to find the correct character within the line (after subtracting the line origin from point
). This works best for simple stacked lines that run the entire view (such as yours).
Other solutions:
Calculate all the line rectangles during drawRect:
and cache them. Then you can just do rectangle checks in touchesEnded:...
. That's a very good solution if drawing is less common than tapping. If drawing is significantly more common than tapping, then that's a bad approach.
Do all your calculations using CTLineGetTypographicBounds()
. That doesn't require a graphics context. You can use this to work out the rectangles.
In drawRect:
generate a CGLayer with the current context and store it in an ivar. Then use the context from the CGLayer
to calculate CTLineGetImageBounds()
. The context from the CGLayer
will be "compatible" with the graphics context you're using to draw.
Side note: Why are you calling UIGraphicsPushContext(context);
in drawRect:
? You're setting the current context to the current context. That doesn't make sense. And I don't see a corresponding UIGraphicsPopContext()
.
Upvotes: 2