سؤال

لدي بعض التعليمات البرمجية في طريقة عرض يرسم بعض النص المنسوب باستخدام Coretext. في هذا ، أنا أبحث عن عناوين URL وجعلها أزرق. الفكرة هي عدم إحضار كل النفقات العامة UIWebView فقط للحصول على روابط قابلة للنقر. بمجرد أن ينقر المستخدم على هذا الرابط (وليس خلية عرض الجدول بأكملها) ، أريد إطلاق طريقة مندوبة سيتم استخدامها بعد ذلك لتقديم عرض مشروط يحتوي على عرض ويب يذهب إلى عنوان URL هذا.

أقوم بحفظ المسار والسلسلة نفسها كمتغيرات مثيل للعرض ، ويحدث رمز الرسم في -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" للطباعة إذا قمت بالنقر فوق عنوان URL.

لست متأكدًا من مكان أخذ هذا الأمر إلى أبعد من ذلك ، فإن الموارد التي بحثت عنها في حل هذه المشكلة هي 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 ، وإضافة هذه الطبقة الجديدة كطبقة فرعية لطبقة وجهات نظرك واختبارها مقابله في اللمسات التي تلمسها؟ إذا قمت بذلك ، فيجب أن تكون قادرًا على إنشاء منطقة يمكن لمسها أكبر عن طريق جعل الطبقة أكبر من النص المرسوم.

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

نسخة سريعة 3 من إجابة 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
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top