美文网首页
CoreText 开发中遇到的小问题

CoreText 开发中遇到的小问题

作者: 剁椒鸡蛋zy | 来源:发表于2018-03-09 10:23 被阅读0次

    1. CoreText 中CTRunGetPosition 函数的小坑

    下面代码为例


    7283612d-a27a-45e9-b769-1b7d513ad3f5-134466.jpg
    1. for循环每次执行到第二个循环的时候,肯定会在NSlog 那里挂掉?
      因为 CTRunGetPosition中第二个参数range,如果range.length == 0的话,是去获取当前CTRun里面每个字形的position,所有第三个参数需要传递的是一个数组。
    2. CTRunGetPosition这个函数返回的字形的position是相对当前其所在的CTLine的,position.y === 0 position.x = 到line左边的距离

    2. UIView的 drawRect:方法

    看下面简单的代码

    - (void)drawRect:(CGRect)rect{
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        
        int s = random() % 2;
        if (s == 1) {
            CGContextSaveGState(ctx);
            CGPathRef path = CGPathCreateWithRect(CGRectMake(10, 40, 50, 30), nil);
            CGContextAddPath(ctx, path);
            CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
            CGContextFillPath(ctx);
            CGContextRestoreGState(ctx);
        }else{
            CGContextSaveGState(ctx);
            CGPathRef path = CGPathCreateWithRect(CGRectMake(100, 100, 50, 30), nil);
            CGContextAddPath(ctx, path);
            CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
            CGContextFillPath(ctx);
            CGContextRestoreGState(ctx);
        }
    }
    

    当我们想让 UIView 去重新绘制界面的时候,会去 调用 setNeedsDisplay 方法,然后系统会去调用 drawRect 绘制,这种情况下回清空以前的内容,重新开始绘制; 但是当调用 setNeedsDisplayInRect: 的时候 并且rect 不等于 UIVIewbounds 的时候,不会清空以前的内容,并且在传入的rect 内重新绘制。

    3.CoreText 中frame代表什么 段落间距到底如何设置分析?

     NSMutableAttributedString *atrriStr = [[NSMutableAttributedString alloc] initWithString:@"一二三四五六七八九十一二三四五六七八九十一二三四567890\n123五六七八九十一二三四五六七八九十一二三四五六七八九十gh"];
     CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)atrriStr);
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL,rect);
        int offset = 28;
        CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, offset), path,nil);
        CTFrameRef frame2 =  CTFramesetterCreateFrame(frameSetter, CFRangeMake(offset, atrriStr.length - offset), path, nil);
    

    上面的代码 生成的结果如下


    26233274-8B83-430B-BFFD-3F428AD9D3F8.png

    可以看到90竟然跑到上面去了,可以总结出:
    1. 一个长的string 生成多个frame 然后每个frame 都调用CTFrameDraw(frame, ctx);去绘制的时候每个frame都是从容器的原点开始绘制的,并不会去应用段落间距样式,CoreText内部还是以换行符为段落分隔符,并不是每个CTFrame就是一个段落。

    测试代码地址
    CoreText段落样式

    4. CoreText关于内存释放的问题

    @interface TextLine : NSObject
    + (instancetype)lineWithCTLine:(CTLineRef)ctline position:(CGPoint)position;
    @property (nonatomic,readonly) CTLineRef ctLine;
    @end
    /**
     处理line 里面的attachment
     */
    - (void)proceeAttachment{
        if (!_ctLine) {
            return;
        }
        self.runRectAry = [NSMutableArray array];
        self.runStrRangeAry = [NSMutableArray array];
        // 遍历line里面的runs,看看那个run有runDelegate,有就表示这个run是图片,然后保存起来
        CFArrayRef runs = CTLineGetGlyphRuns(_ctLine);
        NSInteger runCount = CFArrayGetCount(runs);
        for (int i = 0; i < runCount; i++) {
            CTRunRef run = CFArrayGetValueAtIndex(runs, i);
            CFRange strRange = CTRunGetStringRange(run);
            NSValue *rangeValue = [NSValue valueWithRange:NSMakeRange(strRange.location, strRange.length)];
            [self.runStrRangeAry addObject:rangeValue];
            // ---  下面这段代码把每个run的rect保存起来
            CFIndex glyphCount = CTRunGetGlyphCount(run);
            if (glyphCount == 0) {
                [self.runRectAry addObject:[NSValue valueWithCGRect:CGRectZero]];
            }else{
                CGPoint runPos;
                CTRunGetPositions(run, CFRangeMake(0, 1), &runPos);
                CGFloat ascent;
                CGFloat descent;
                CGFloat leading;
                CGFloat runWidth;
                runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
                CGRect rect = CGRectMake(runPos.x, runPos.y, runWidth, ascent + descent);
                NSValue *v = [NSValue valueWithCGRect:rect];
                if (v) {
                    [self.runRectAry addObject:v];
                }
            }
            // ---
            CFDictionaryRef runAttri = CTRunGetAttributes(run);
            CTRunDelegateRef runDelegate = CFDictionaryGetValue(runAttri, kCTRunDelegateAttributeName);
    //        FuTextRun *attachment = (__bridge FuTextRun *)CTRunDelegateGetRefCon(runDelegate);
            GWWTextAttachmentCallbackModel *model = (__bridge GWWTextAttachmentCallbackModel *)CTRunDelegateGetRefCon(runDelegate);
            if (!model || model.type == GWWTextAttachmentTypeBlack) {
                continue;
            }
            CGPoint runPoint; // 这个point 是相对于当前line里面计算的
            CTRunGetPositions(run, CFRangeMake(0, 0), &runPoint);
            // 计算附件的位置信息,如果附件图片比当前行小,就居中,大就底部对齐
            GWWTextAttachment *txtAttach = [[GWWTextAttachment alloc] init];
            txtAttach.srcUrl = model.originalString;
            txtAttach.rect =  (CGRect){runPoint,model.size};
            txtAttach.position = runPoint;
            CFRange range = CTRunGetStringRange(run);
            txtAttach.rangeOfOriginString = NSMakeRange(range.location, range.length);
            // 谨记 这个不能释放,因为runAttri 取到的数据是包含在run里面的导致以后释放line的时候遇到
            // [CFDictionary release]: message sent to deallocated instance 0x1c806fa80
            // 问题
    //        CFRelease(runAttri);
    //        CFRelease(runDelegate);
            [self.attachmentAry addObject:txtAttach];
        }
    }
    
    - (void)dealloc{
    // 对象释放的时候要记得释放ctline
        if (_ctLine) {
          CFRelease(_ctLine);
        }
    }
    

    还有设置attributeString的属性样式的时候也要记得release

        CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(setting, sizeof(setting) / sizeof(CTParagraphStyleSetting));
        [attributes setObject:(__bridge id)paragraphStyle forKey:(id)kCTParagraphStyleAttributeName];
        CFRelease(paragraphStyle);
    
     CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, (__bridge_retained void *)model);
        [string addAttributes:@{(id)kCTRunDelegateAttributeName: (__bridge id)runDelegate} range:NSMakeRange(0, string.length)];
        CFRelease(runDelegate);
    
    para泄漏 2.png

    相关文章

      网友评论

          本文标题:CoreText 开发中遇到的小问题

          本文链接:https://www.haomeiwen.com/subject/brkpfftx.html