tunsuy
7/19/2016 - 2:56 AM

绘制富文本在context上

绘制富文本在context上

- (void)setContentsWithURLString:(NSString *)urlString {

    self.contents = (__bridge id _Nullable)([UIImage imageNamed:@"placeholder"].CGImage);
    @weakify(self)
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    [manager downloadImageWithURL:[NSURL URLWithString:urlString]
                          options:SDWebImageCacheMemoryOnly
                         progress:nil
                        completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                            if (image) {
                                @strongify(self)
                                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                                    if (!_observer) {

                                        _observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
                                            self.contents = (__bridge id _Nullable)(image.CGImage);
                                        });

                                        if (_observer) {
                                            CFRunLoopAddObserver(CFRunLoopGetMain(), _observer,  kCFRunLoopCommonModes);
                                        }
                                    }
                                });
                                self.originImage = image;
                            }
                        }];
}
- (NSMutableAttributedString *)highlightText:(NSMutableAttributedString *)coloredString{
    // 创建带高亮的AttributedString
    NSString* string = coloredString.string;
    NSRange range = NSMakeRange(0,[string length]);
    NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
    NSArray *matches = [linkDetector matchesInString:string options:0 range:range];

    for(NSTextCheckingResult* match in matches) {
        [self.ranges addObject:NSStringFromRange(match.range)];
        UIColor *highlightColor = UIColorFromRGB(0x297bc1);
        [coloredString addAttribute:(NSString*)kCTForegroundColorAttributeName
                              value:(id)highlightColor.CGColor range:match.range];
    }

    return coloredString;
}

- (void)drawFramesetter:(CTFramesetterRef)framesetter
       attributedString:(NSAttributedString *)attributedString
              textRange:(CFRange)textRange
                 inRect:(CGRect)rect
                context:(CGContextRef)c {
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL);

    CGFloat ContentHeight = CGRectGetHeight(rect);
    CFArrayRef lines = CTFrameGetLines(frame);
    NSInteger numberOfLines = CFArrayGetCount(lines);

    CGPoint lineOrigins[numberOfLines];
    CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins);

    // 遍历每一行
    for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {
        CGPoint lineOrigin = lineOrigins[lineIndex];
        CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);

        CGFloat descent = 0.0f, ascent = 0.0f, lineLeading = 0.0f;
        CTLineGetTypographicBounds((CTLineRef)line, &ascent, &descent, &lineLeading);

        CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, NSTextAlignmentLeft, rect.size.width);
        CGFloat y = lineOrigin.y - descent - self.font.descender;

        // 设置每一行位置
        CGContextSetTextPosition(c, penOffset + self.xOffset, y - self.yOffset);
        CTLineDraw(line, c);

        // CTRunRef同一行中文本的不同样式,包括颜色、字体等,此处用途为处理链接高亮
        CFArrayRef runs = CTLineGetGlyphRuns(line);
        for (int j = 0; j < CFArrayGetCount(runs); j++) {
            CGFloat runAscent, runDescent, lineLeading1;

            CTRunRef run = CFArrayGetValueAtIndex(runs, j);
            NSDictionary *attributes = (__bridge NSDictionary*)CTRunGetAttributes(run);
            // 判断是不是链接
            if (!CGColorEqualToColor((__bridge CGColorRef)([attributes valueForKey:@"CTForegroundColor"]), self.textColor.CGColor)) {
                CFRange range = CTRunGetStringRange(run);
                float offset = CTLineGetOffsetForStringIndex(line, range.location, NULL);

                // 得到链接的CGRect
                CGRect runRect;
                runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, &lineLeading1);
                runRect.size.height = self.font.lineHeight;
                runRect.origin.x = lineOrigin.x + offset+ self.xOffset;
                runRect.origin.y = lineOrigin.y;
                runRect.origin.y -= descent + self.yOffset;

                // 因为坐标系被翻转,链接正常的坐标需要通过CGAffineTransform计算得到
                CGAffineTransform transform = CGAffineTransformMakeTranslation(0, ContentHeight);
                transform = CGAffineTransformScale(transform, 1.f, -1.f);
                CGRect flipRect = CGRectApplyAffineTransform(runRect, transform);

                // 保存是链接的CGRect
                NSRange nRange = NSMakeRange(range.location, range.length);
                self.framesDict[NSStringFromRange(nRange)] = [NSValue valueWithCGRect:flipRect];

                // 保存同一条链接的不同CGRect,用于点击时背景色处理
                for (NSString *rangeString in self.ranges) {
                    NSRange range = NSRangeFromString(rangeString);
                    if (NSLocationInRange(nRange.location, range)) {
                        NSMutableArray *array = self.relationDict[rangeString];
                        if (array) {
                            [array addObject:NSStringFromCGRect(flipRect)];
                            self.relationDict[rangeString] = array;
                        } else {
                            self.relationDict[rangeString] = [NSMutableArray arrayWithObject:NSStringFromCGRect(flipRect)];
                        }
                    }
                }

            }
        }
    }

    CFRelease(frame);
    CFRelease(path);
}
- (void)drawInContext:(CGContextRef)context withPosition:(CGPoint)p andFont:(UIFont *)font andTextColor:(UIColor *)color andHeight:(float)height andWidth:(float)width lineBreakMode:(CTLineBreakMode)lineBreakMode {
    CGSize size = CGSizeMake(width, height);
    // 翻转坐标系
    CGContextSetTextMatrix(context,CGAffineTransformIdentity);
    CGContextTranslateCTM(context,0,height);
    CGContextScaleCTM(context,1.0,-1.0);

    NSMutableDictionary * attributes = [StringAttributes attributeFont:font andTextColor:color lineBreakMode:lineBreakMode];

    // 创建绘制区域(路径)
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path,NULL,CGRectMake(p.x, height-p.y-size.height,(size.width),(size.height)));

    // 创建AttributedString
    NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:self attributes:attributes];
    CFAttributedStringRef attributedString = (__bridge CFAttributedStringRef)attributedStr;

    // 绘制frame
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CTFrameRef ctframe = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0),path,NULL);
    CTFrameDraw(ctframe,context);
    CGPathRelease(path);
    CFRelease(framesetter);
    CFRelease(ctframe);
    [[attributedStr mutableString] setString:@""];
    CGContextSetTextMatrix(context,CGAffineTransformIdentity);
    CGContextTranslateCTM(context,0, height);
    CGContextScaleCTM(context,1.0,-1.0);
}