2010-09-27 14 views
9

Tôi có một số mã trong một chế độ xem để vẽ một số văn bản được phân bổ bằng cách sử dụng CoreText. Trong đó, tôi đang tìm kiếm các url và biến chúng thành màu xanh lam. Ý tưởng là không mang lại tất cả các chi phí của một UIWebView chỉ để có được các liên kết có thể nhấp. Khi người dùng chạm vào liên kết đó (không phải toàn bộ ô xem bảng), tôi muốn kích hoạt phương thức ủy nhiệm, sau đó sẽ được sử dụng để trình bày chế độ xem phương thức có chứa chế độ xem web sẽ chuyển đến url đó.Sử dụng CoreText và chạm để tạo một hành động có thể nhấp

Tôi đang lưu đường dẫn và chuỗi chính nó làm biến mẫu của chế độ xem và mã vẽ sẽ xảy ra trong -drawRect: (tôi đã để nó ngắn gọn).

Trình xử lý cảm ứng của tôi tuy nhiên, trong khi không hoàn tất, không in được những gì tôi mong đợi. Dưới đây là:

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

Đây không phải là mã đẹp nhất hiện tại, tôi vẫn đang cố gắng làm cho nó hoạt động, vì vậy hãy tha thứ cho chất lượng mã.

Vấn đề tôi gặp phải là khi tôi nhấn vào bên ngoài liên kết, những gì tôi mong đợi sẽ xảy ra, xảy ra: Không có gì bị sa thải.

Tuy nhiên, tôi mong đợi "Tapped line" để được in nếu tôi chạm vào cùng một dòng liên kết đang bật, điều này không xảy ra và tôi mong đợi cả hai "Tapped line""Tapped run" để in ra nếu tôi nhấn vào URL.

Tôi không chắc chắn về nơi cần thực hiện thêm, tài nguyên tôi đã xem xét để giải quyết vấn đề này là Cocoa cụ thể (gần như hoàn toàn không thể áp dụng) hoặc thiếu thông tin về trường hợp cụ thể này.

Tôi sẽ sẵn sàng xem các tài liệu hướng dẫn chi tiết cách phát hiện chính xác nếu một liên lạc xảy ra trong giới hạn của bản vẽ lõi trên mã, nhưng tại thời điểm này, tôi chỉ muốn giải quyết vấn đề này, Trợ giúp sẽ được đánh giá cao.

CẬP NHẬT: Tôi đã thu hẹp sự cố của mình xuống vấn đề tọa độ. Tôi đã lật các tọa độ (và không như được hiển thị ở trên) và vấn đề tôi nhận được là chạm vào đăng ký như tôi mong đợi, nhưng không gian tọa độ được lật, và tôi dường như không thể lật ngược nó.

+0

Bạn đã đưa vào tài khoản thực tế là CoreText sử dụng một hệ toạ độ lật? Ngay bây giờ có vẻ như tôi đang so sánh mọi thứ trong hai hệ thống tọa độ khác nhau. – Jacques

+0

Ngoài ra, đó là một ý tưởng tồi để tái tạo khung hình trên mọi cảm ứng như thế. Tạo khung hình khung là rất tốn kém, vì vậy bạn nên lưu nó khi lần đầu tiên vẽ hoặc thiết lập văn bản. – Jacques

+0

Phương pháp phát triển của tôi chỉ đơn giản là: 1) Làm cho nó hoạt động; 2) Làm cho nó đúng; 3) Làm cho nó nhanh/sạch. Hãy đối phó với # 2 khi chúng tôi nhận đượC# 1 đi :) Cũng liên quan đến hệ thống tọa độ, có tôi nhận ra rằng, và trong một thời gian tôi đã không xử lý nó.Trong đoạn mã ngay bây giờ, sau khi tham khảo ý kiến ​​của một đồng nghiệp, anh ấy đã đặt tôi thẳng thắn về điều này, và ít nhất nó cũng phát hiện các vòi trên các dòng, không phải là chạy. Vẫn cố gắng tìm ra điều đó. – jer

Trả lời

8

Tôi vừa thực hiện việc này để lấy chỉ số ký tự chuỗi từ vị trí cảm ứng. Số dòng sẽ là tôi trong trường hợp này:

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

Cảm ơn Nick bạn đã cứu tôi! :) – stefanosn

+0

Điều này làm việc tuyệt vời, tôi có thể so sánh điều này với một tập hợp các phạm vi mà tôi đã lưu trữ cho các từ có thể nhấn. Điều này là nhanh hơn nhiều so với cách tiếp cận cũ của tôi - cảm ơn bạn :) –

+0

Làm thế nào tôi có thể nhận được ctFrame? –

0

Bạn có thể thử thêm bản vẽ văn bản của mình vào CALayer, thêm lớp mới này làm lớp con của lớp chế độ xem của bạn và hittest chống lại nó trong chạm của bạnEnded? Nếu bạn làm như vậy, bạn sẽ có thể tạo một vùng có thể chạm lớn hơn bằng cách làm cho lớp lớn hơn văn bản được vẽ.

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

Sau đó, tôi muốn có một vấn đề khác của việc phải chia văn bản của tôi thành hai lớp khác nhau - một lớp mà tôi muốn kích hoạt đại biểu, và sau đó phần còn lại. Vấn đề tôi muốn gặp phải ở đó là điều gì sẽ xảy ra nếu URL nằm ở giữa một dòng và có văn bản trước và sau nó? Sau đó tôi phải chia thành nhiều lớp khác nhau. Nó có thể bị lông nếu có một vài URL trong nhóm văn bản tôi render. Tôi muốn chỉ cần lấy một danh sách của tất cả các mục trong chuỗi attrib tôi đã có màu xanh, và nhận được hộp giới hạn của họ, xem nếu người dùng khai thác bên trong đó và gọi các đại biểu với url đó. – jer

+0

Đúng, điều này có thể là một vấn đề. Có lẽ tôi không hiểu vấn đề của bạn. Làm thế nào về việc giữ văn bản như nó và lớp phủ một lớp hittable để nắm bắt những chạm? – msg

+0

Tôi vẫn cần phải biết chính xác nơi để đặt nó - có nghĩa là tôi vẫn cần phải có được trực tiếp của URL. Điều đó đặt tôi trở lại hình vuông. :) – jer

0

Swift 3 phiên bản của câu trả lời Nick H247 của:

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 
     } 
    } 
} 
Các vấn đề liên quan