CoreText實現圖文混排和點擊事件

來源:互聯網
上載者:User

本例子是實作類別似於微博的富文字效果,可以實現圖文混排和處理點擊事件觸發。使用CoreText進行圖文混排的核心思想是把需要擺放圖片的位置用Null 字元替換原來的字元,並且實現CTRunDelegate,用於動態設定Null 字元的高度和寬度(代表圖片的大小),並且對這些Null 字元設定一個屬性名稱來區別於其他CTRun,之後進行圖片渲染的時候就能通過該屬性來區分哪些Null 字元是代表圖片的預留位置,哪些是普通的Null 字元。使用CoreText處理點擊事件的關鍵是判斷點擊的位置是本文內容中的第幾個字元,然後通過判斷該字元是否在需要處理點擊事件的字串範圍內。

 


//建立NSMutableAttributedString,解析所有觸發點擊事件和替換所有需要顯示圖片的位置
-(void)buildAttribute{
   
    content = [[NSMutableAttributedString alloc]initWithString:originalStr];
    //建立圖片的名字
    NSString *imgName = @"smile.png";
    //設定CTRun的回調,用於針對需要被替換成圖片的位置的字元,可以動態設定圖片預留位置的寬高
    CTRunDelegateCallbacks imageCallbacks;
    imageCallbacks.version = kCTRunDelegateVersion1;
    imageCallbacks.dealloc = RunDelegateDeallocCallback;
    imageCallbacks.getAscent = RunDelegateGetAscentCallback;
    imageCallbacks.getDescent = RunDelegateGetDescentCallback;
    imageCallbacks.getWidth = RunDelegateGetWidthCallback;
    //建立CTRun回調
    CTRunDelegateRef runDelegate = CTRunDelegateCreate(&imageCallbacks, imgName);
    //這裡為了簡化解析文字,所以直接認為最後一個字元是需要顯示圖片的位置,對需要顯示圖片的位置,都用Null 字元來替換原來的字元,空格用於給圖片留位置
    NSMutableAttributedString *imageAttributedString = [[NSMutableAttributedString alloc] initWithString:@" "];
    //設定圖片預留字元使用CTRun回調
    [imageAttributedString addAttribute:(NSString *)kCTRunDelegateAttributeName value:(id)runDelegate range:NSMakeRange(0, 1)];
    CFRelease(runDelegate);
    //設定圖片預留字元使用一個imageName的屬性,區別於其他字元
    [imageAttributedString addAttribute:@"imageName" value:imgName range:NSMakeRange(0, 1)];
    [content appendAttributedString:imageAttributedString];
    //換行模式,設定段落屬性
    CTParagraphStyleSetting lineBreakMode;
    CTLineBreakMode lineBreak = kCTLineBreakByCharWrapping;
    lineBreakMode.spec = kCTParagraphStyleSpecifierLineBreakMode;
    lineBreakMode.value = &lineBreak;
    lineBreakMode.valueSize = sizeof(CTLineBreakMode);
    CTParagraphStyleSetting settings[] = {
        lineBreakMode
    };
    CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 1);
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)style forKey:(id)kCTParagraphStyleAttributeName ];
    [content addAttributes:attributes range:NSMakeRange(0, [content length])];
    //這裡對需要進行點擊事件的字元heightlight效果,這裡簡化解析過程,直接hard code需要heightlight的範圍
    [content addAttribute:(id)kCTForegroundColorAttributeName value:(id)[[UIColor blueColor]CGColor] range:NSMakeRange(0, 10)];
}
//CTRun的回調,銷毀記憶體的回調
void RunDelegateDeallocCallback( void* refCon ){
   
}

//CTRun的回調,擷取高度
CGFloat RunDelegateGetAscentCallback( void *refCon ){
    NSString *imageName = (NSString *)refCon;
    return 30;//[UIImage imageNamed:imageName].size.height;
}

CGFloat RunDelegateGetDescentCallback(void *refCon){
    return 0;
}
//CTRun的回調,擷取寬度
CGFloat RunDelegateGetWidthCallback(void *refCon){
    NSString *imageName = (NSString *)refCon;
    return 30;//[UIImage imageNamed:imageName].size.width;
}
- (void)drawRect:(CGRect)rect
{
    //設定NSMutableAttributedString的所有屬性
    [self buildAttribute];
    NSLog(@"rect:%@",NSStringFromCGRect(rect));
    CGContextRef context = UIGraphicsGetCurrentContext();
    //設定context的ctm,用於適應core text的座標體系
    CGContextSaveGState(context);
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, rect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    //設定CTFramesetter
    CTFramesetterRef framesetter =  CTFramesetterCreateWithAttributedString((CFAttributedStringRef)content);
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, CGRectMake(0, 0, rect.size.width, rect.size.height));
    //建立CTFrame
    _frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, content.length), path, NULL);
    //把文字內容繪製出來
    CTFrameDraw(_frame, context);
    //擷取畫出來的內容的行數
    CFArrayRef lines = CTFrameGetLines(_frame);
    //擷取每行的原點座標
    CGPoint lineOrigins[CFArrayGetCount(lines)];
    CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), lineOrigins);
    NSLog(@"line count = %ld",CFArrayGetCount(lines));
    for (int i = 0; i < CFArrayGetCount(lines); i++) {
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        CGFloat lineAscent;
        CGFloat lineDescent;
        CGFloat lineLeading;
        //擷取每行的寬度和高度
        CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
        NSLog(@"ascent = %f,descent = %f,leading = %f",lineAscent,lineDescent,lineLeading);
        //擷取每個CTRun
        CFArrayRef runs = CTLineGetGlyphRuns(line);
        NSLog(@"run count = %ld",CFArrayGetCount(runs));
        for (int j = 0; j < CFArrayGetCount(runs); j++) {
            CGFloat runAscent;
            CGFloat runDescent;
            CGPoint lineOrigin = lineOrigins[i];
            //擷取每個CTRun
            CTRunRef run = CFArrayGetValueAtIndex(runs, j);
            NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
            CGRect runRect;
            //調整CTRun的rect
            runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
            NSLog(@"width = %f",runRect.size.width);
           
            runRect=CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runRect.size.width, runAscent + runDescent);
           
            NSString *imageName = [attributes objectForKey:@"imageName"];
            //圖片渲染邏輯,把需要被圖片替換的字元位置畫片
            if (imageName) {
                UIImage *image = [UIImage imageNamed:imageName];
                if (image) {
                    CGRect imageDrawRect;
                    imageDrawRect.size = CGSizeMake(30, 30);
                    imageDrawRect.origin.x = runRect.origin.x + lineOrigin.x;
                    imageDrawRect.origin.y = lineOrigin.y;
                    CGContextDrawImage(context, imageDrawRect, image.CGImage);
                }
            }
        }
    }
    CGContextRestoreGState(context);
}
//接受觸摸事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    //擷取UITouch對象
    UITouch *touch = [touches anyObject];
    //擷取觸摸點擊當前view的座標位置
    CGPoint location = [touch locationInView:self];
    NSLog(@"touch:%@",NSStringFromCGPoint(location));
    //擷取每一行
    CFArrayRef lines = CTFrameGetLines(_frame);
    CGPoint origins[CFArrayGetCount(lines)];
    //擷取每行的原點座標
    CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), origins);
    CTLineRef line = NULL;
    CGPoint lineOrigin = CGPointZero;
    for (int i= 0; i < CFArrayGetCount(lines); i++)
    {
        CGPoint origin = origins[i];
        CGPathRef path = CTFrameGetPath(_frame);
        //擷取整個CTFrame的大小
        CGRect rect = CGPathGetBoundingBox(path);
        NSLog(@"origin:%@",NSStringFromCGPoint(origin));
        NSLog(@"rect:%@",NSStringFromCGRect(rect));
        //座標轉換,把每行的原點座標轉換為uiview的座標體系
        CGFloat y = rect.origin.y + rect.size.height - origin.y;
        NSLog(@"y:%f",y);
        //判斷點擊的位置處於那一行範圍內
        if ((location.y <= y) && (location.x >= origin.x))
        {
            line = CFArrayGetValueAtIndex(lines, i);
            lineOrigin = origin;
            break;
        }
    }
   
    location.x -= lineOrigin.x;
    //擷取點擊位置所處的字元位置,就是相當於點擊了第幾個字元
    CFIndex index = CTLineGetStringIndexForPosition(line, location);
    NSLog(@"index:%ld",index);
    //判斷點擊的字元是否在需要處理點擊事件的字串範圍內,這裡是hard code了需要觸發事件的字串範圍
    if (index>=1&&index<=10) {
        UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"click event" message:[originalStr substringWithRange:NSMakeRange(0, 10)] delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil];
        [alert show];
    }

}

如下:


 


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.