使用CoreText并触摸来创建可点击的动作
我在视图中使用CoreText绘制了一些属性文本中的一些代码。 在这里,我正在寻找网址并将它们变成蓝色。 这个想法是不引入UIWebView
所有开销,只是为了获得可点击的链接。 一旦用户点击该链接(而不是整个表格视图单元格),我想要引发一个委托方法,然后用它来呈现一个模式视图,其中包含一个到该URL的Web视图。
我将路径和字符串本身保存为视图的实例变量,并且绘图代码发生在-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"
如果我轻触即可印刷网址。
我不确定在哪里可以进一步采取这些措施,为解决这个问题我所研究的资源是Cocoa特定的(这几乎完全不适用),或者缺乏关于这个特定情况的信息。
我会很高兴地指出文档的详细内容,详细说明如何正确地检测触摸是否发生在代码核心文本画面的边界内,但在这一点上,我只是想解决这个问题,所以任何帮助都会很大赞赏。
更新 :我已经缩小了我的问题到一个协调问题。 我翻转了坐标(而不是如上所示),我得到的问题是触摸按我的预期注册,但坐标空间翻转,我似乎无法将其翻转。
我刚刚做了这个从触摸位置获取字符串字符索引。 行号是我在这种情况下:
- (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中,将这个新图层添加为视图图层的子图层,并在touchesEnded中针对它进行测试。 如果你这样做,你应该能够创建一个更大的可触摸区域,使图层大于绘制文本。
// hittesting
UITouch *touch = [[event allTouches] anyObject];
touchedLayer = (CALayer *)[self.layer hitTest:[touch locationInView:self]];
Swift 3版尼克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
}
}
}
链接地址: http://www.djcxy.com/p/81981.html
上一篇: Using CoreText and touches to create a clickable action