代码之家  ›  专栏  ›  技术社区  ›  jer

使用CoreText和Touchs创建可单击的操作

  •  9
  • jer  · 技术社区  · 14 年前

    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 run" 如果我点击网址就会被打印出来。

    更新 :我已将问题缩小为协调问题。我已经翻转了坐标(并没有如上所示),我得到的问题是,我接触寄存器,因为我期望,但坐标空间是翻转,我似乎不能翻转回来。

    3 回复  |  直到 14 年前
        1
  •  9
  •   Nick Hingston    12 年前

    我刚刚这样做是为了从touch位置获取字符串索引。在这种情况下,行号为i:

    - (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);
    }
    
        2
  •  0
  •   msg    14 年前

    你可以试着把你的文本图形添加到一个CALayer中,把这个新图层作为你的视图图层的子图层,然后在你的touchesend中对它进行hittest?如果您这样做,您应该能够创建一个更大的可触摸区域,使层大于绘制的文本。

    // hittesting 
    UITouch *touch = [[event allTouches] anyObject];
    touchedLayer = (CALayer *)[self.layer hitTest:[touch locationInView:self]];
    
        3
  •  0
  •   KristopherGBaker    8 年前

    尼克H247答案的Swift 3版本:

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