CoreText使用(2)图文混排

来源:互联网 发布:86joy平台 网络 编辑:程序博客网 时间:2024/06/06 03:07
在一个UIVIew的子空间上实现图文混排。支持本地图片和网络图片显示 不支持图片点击监听的功能。
CoreText从绘制纯文本到绘制图片 使用NSAttributedString 但是图片实现是用一个空白字符作为NSAttributedString的占位符 然后设置代理 告诉Core Text给该占位字符留出一定宽度高度 最后把图片绘制到预留位置。
思路:
网络图片没有下载完 先绘制占位图片 为了方便 占位图直接使用一张本地图片。然后去下载图片 等下再完成 调用uiview的setNeedDisplay进行重绘。
需要注意:本地图片可以拿到其宽度和高度 对于网络图片 在下载完成之后不知道宽度高度 往往会采用在url后面拼接上宽度高度信息方式来处理。
绘制文本上一篇已经说过了,不再赘述。今天关注点主要在图片上。
直接给出绘制的代码:
drawRect方法
-(void)drawRect:(CGRect)rect{//    NSLog(@"%@",rect);    CGContextRef context = UIGraphicsGetCurrentContext();    CGContextSetTextMatrix(context, CGAffineTransformIdentity);    CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, self.bounds.size.height));    CGMutablePathRef path = CGPathCreateMutable();    CGPathAddRect(path, NULL, self.bounds);    //文本    NSString * str = @"小戎俴收,五楘梁辀。游环胁驱,阴靷鋈续。文茵畅毂,驾我骐馵。言念君子,温其如玉。在其板屋,乱我心曲";    NSMutableAttributedString * attr = [[NSMutableAttributedString alloc]initWithString:str];    [attr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:30] range:NSMakeRange(0, 6)];        // 两种方式皆可    [attr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(3, 10)];    [attr addAttribute:(id)kCTForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(0, 2)];        #pragma mark -- 本地图片设置部分    NSString * imageHolder = @"xiaopang.jpg";        //代理结构体初始化    CTRunDelegateCallbacks imageCallbacks;    imageCallbacks.version = kCTRunDelegateVersion1;    imageCallbacks.dealloc = myRunDelegateDeallocCallback;    imageCallbacks.getAscent = myRunDelegateGetAscentCallback;    imageCallbacks.getDescent =myRunDelegateGetDscentCallback;    imageCallbacks.getWidth = myRunDelegateGetWidthCallback;        //构建CTRun的代理    CTRunDelegateRef delegate = CTRunDelegateCreate(&imageCallbacks, (__bridge void * _Nullable)(imageHolder));        //构建占位属性文本    NSMutableAttributedString * imgeAttr = [[NSMutableAttributedString alloc]initWithString:@" "];    //设置占位属性文本的代理为delegate    [imgeAttr addAttribute:kCTRunDelegateAttributeName value:(__bridge id)delegate range:NSMakeRange(0,1)];    CFRelease(delegate);    //这个是作为id选择器的    [imgeAttr addAttribute:@"imageName" value:imageHolder range:NSMakeRange(0, 1)];    //将占位属性文本插入到文本中去。    [attr insertAttributedString:imgeAttr atIndex:5];                // 图片信息字典    NSString *picURL =@"http://img3.imgtn.bdimg.com/it/u=1811953530,4106413237&fm=206&gp=0.jpg";    NSDictionary *imgInfoDic = @{@"width":@192,@"height":@277}; // 宽高跟具体图片有关    // 设置CTRun的代理    CTRunDelegateRef mydelegate = CTRunDelegateCreate(&imageCallbacks, (__bridge void *)imgInfoDic);        // 使用0xFFFC作为空白的占位符    unichar objectReplacementChar = 0xFFFC;    NSString *content = [NSString stringWithCharacters:&objectReplacementChar length:1];    NSMutableAttributedString *space = [[NSMutableAttributedString alloc] initWithString:content];    [space addAttribute:kCTRunDelegateAttributeName value:(__bridge id _Nonnull)(mydelegate) range:NSMakeRange(0, 1)];    [space addAttribute:@"imageName" value:picURL range:NSMakeRange(0, 1)];    [space addAttribute:@"dict" value:imgInfoDic range:NSMakeRange(0, 1)];    CFRelease(mydelegate);        // 将创建的空白AttributedString插入进当前的attrString中,位置可以随便指定,不能越界    [attr insertAttributedString:space atIndex:10];            CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attr);        _frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [attr length]), path, NULL);    CTFrameDraw(_frame, context);            //图片绘制逻辑    [self drawAttachments];        //    CFRelease(_frame);    CFRelease(framesetter);    CFRelease(path);}
代理方法:

void myRunDelegateDeallocCallback(void *refCon){    NSLog(@"%@",@"Rundelegate dealloc");}CGFloat myRunDelegateGetDscentCallback(void *refCon){    NSLog(@"%@",@"get descent");    return 0;}#pragma mark -- 获取宽度CGFloat myRunDelegateGetWidthCallback(void *refCon){    NSLog(@"%@",@"get width");//    return 10;    NSString *imageName = (__bridge NSString *)refCon;        if ([imageName isKindOfClass:[NSString class]])    {        // 本地图片        return [UIImage imageNamed:imageName].size.width;    }            // 对应网络图片    return [[(__bridge NSDictionary *)refCon objectForKey:@"width"] floatValue];}#pragma mark -- 获取高度CGFloat myRunDelegateGetAscentCallback(void *refCon){    NSLog(@"%@",@"get ascent");//    return 30;    NSString *imageName = (__bridge NSString *)refCon;        if ([imageName isKindOfClass:[NSString class]])    {        // 对应本地图片        return [UIImage imageNamed:imageName].size.height;    }        // 对应网络图片    return [[(__bridge NSDictionary *)refCon objectForKey:@"height"] floatValue];}





图片绘制
-(void)drawAttachments{    CGContextRef context = UIGraphicsGetCurrentContext();    CFArrayRef lines = CTFrameGetLines(_frame);    CFIndex lineCount = CFArrayGetCount(lines);    CGPoint lineOrigins[lineCount];    CTFrameGetLineOrigins(_frame, CFRangeMake(0, 0), lineOrigins);    for (CFIndex i = 0; i < lineCount; i ++) {        CTLineRef line = CFArrayGetValueAtIndex(lines, i);        CGPoint origin = lineOrigins[i];        CGFloat lineAscent;        CGFloat lineDescent;        CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, NULL);        CFArrayRef runs = CTLineGetGlyphRuns(line);        for (int j = 0; j < CFArrayGetCount(runs); j ++) {            //遍历run            CGFloat runAscent;            CGFloat runDescent;            CTRunRef curRun = CFArrayGetValueAtIndex(runs, j);            NSDictionary * attrs = (NSDictionary *)CTRunGetAttributes(curRun);            CGRect runRect;            runRect.size.width = CTRunGetTypographicBounds(curRun, CFRangeMake(0, 0), &lineAscent, &lineDescent, NULL);            runRect = CGRectMake(origin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(curRun).location, NULL), origin.y - runDescent, runRect.size.width, runAscent + runDescent);            NSString * name = [attrs objectForKey:@"imageName"];            NSLog(@"name == %@",name);            if ([name isKindOfClass:[NSString class]]) {                                if ([name isEqualToString:@"xiaopang.jpg"]) {                    //绘制本地图片                    UIImage *img = [UIImage imageNamed:name];                    CGRect imgDrawRect;                    imgDrawRect.size = img.size;                    imgDrawRect.origin.x = runRect.origin.x;                    imgDrawRect.origin.y = origin.y;                    CGContextDrawImage(context, imgDrawRect, img.CGImage);                                        NSLog(@"%@",NSStringFromCGRect(imgDrawRect));                }                else{                    NSLog(@"绘制网络图片");                    CTRunDelegateRef del = (__bridge  CTRunDelegateRef)[attrs objectForKey:(__bridge id)kCTRunDelegateAttributeName];                    if (!del) {                        break;                    }                    UIImage * image;                    if (!_myImage) {                        image = [UIImage imageNamed:@"xiaopang.jpg"];                                                [self downloadImageWithURL:[NSURL URLWithString:name]];                    }                    else{                        image = _myImage;                    }                    //网络图片                    CGRect myRect;                    NSDictionary * dict = (NSDictionary *)[attrs objectForKey:@"dict"];                    myRect.size.height =[[dict objectForKey:@"height"] floatValue];                    myRect.size.width = [[dict objectForKey:@"width"]floatValue];                    myRect.origin.x = runRect.origin.x;                    myRect.origin.y = origin.y;                    NSMutableArray * arr = [NSMutableArray array];                    [arr addObject:[NSValue valueWithPointer:context]];                    [arr addObject:image];                    [arr addObject:NSStringFromCGRect(myRect)];//                    [self performSelectorOnMainThread:@selector(drawImage:) withObject:arr waitUntilDone:NO];                    CGContextDrawImage(context, myRect, image.CGImage);                }            }        }    }}

下载图片的方法
-(void)downloadImageWithURL:(NSURL *)url{    __weak typeof(self) weakself = self;    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        NSData * data = [NSData dataWithContentsOfURL:url];        _myImage = [UIImage imageWithData:data];        NSLog(@"%@",@"下载完毕 设置图片");        if (_myImage) {            NSLog(@"%@",@"sadsga");            dispatch_async(dispatch_get_main_queue(), ^{                [weakself setNeedsDisplay];            });        }    });}

这里涉及到几个点,
1.网络图片需用本地图片占位 否则下载完毕绘图之后会出现位置不对的情况
2.setNeedsDisplay方法会直接通知view调用drawRect方法刷新。
setNeedsDisplay方法和setNeedsLayout后期会讲到。
执行结果:


0 0