CoreText使用(2)图文混排

在一个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后期会讲到。
执行结果:

CoreText使用(2)图文混排_第1张图片

你可能感兴趣的:(CoreText使用(2)图文混排)