zoukankan      html  css  js  c++  java
  • 总结用CoreText绘制文本时遇到的问题以及解决办法

    关于CoreText不做解释。用的人自然知道这个是干什么的。

    功能非常强大,可以绘制文本,图片等。

    这次用的Xcode7.0的版本。所以之前很多方法,现在不能用。也不是不能用,就是有黄色警告很不爽。

    这次要实现在的功能是把一套试题绘制在PDF上面。仅是选择题。

    CoreText是MacOS的框架。坐标是以左下为原点。与我们要用的APP应用的坐标有所不同,绘制的时候,关于坐标的问题,也是让我非常头痛与纠结的一个问题。

    看了网络上面各种办法,一般是转换坐标系。把原点转换到左上。

    代码如下:

    //    CGContextSetTextMatrix(pdfContext, CGAffineTransformIdentity); //它可以用来为每一个显示的字形单独设置变形矩阵。设置字形变换矩阵为CGAffineTransformIdentity,也就是说每一个字形都不做图形变换
        //转换Y轴坐标,  底层坐标与cocoa 组件不同 Y轴相反
    //    CGContextTranslateCTM(pdfContext, 0, CGRectGetHeight(self.view.bounds));
    //    CGContextScaleCTM(pdfContext, 1, -1);

    之所以打了//,是因为我并没有用这个。当初也是试过的。后来发现没用,不知道是我的方法没对还是怎么的。要是有人能指导一下就好了。

    下面说一下我绘制的一些步骤以及要注意的地方,方便自己以后查阅,同时为新入门的同学做个参考。

    绘制的主要代码如下:

    有些地方是可以优化的,但是先就这样了。那个pageRect是整个页面的大小,在绘制的时候,没有用到这个参数。

    值得注意的是,CoreText绘制的是带属性的字符串,关于其释义,可以百度。

    - (void)drawTextWithPageRect:(CGRect)pageRect andContext:(CGContextRef)context andText:(NSString *)text byDrawInRect:(CGRect)inRect{
        CGMutablePathRef pathRef = CGPathCreateMutable(); //1  这里你需要创建一个用于绘制文本的路径区域
        CGPathAddRect(pathRef, NULL, inRect);
        //去掉空行
        
    //    NSString *labelString = text;
    //    NSString *myString = [labelString stringByReplacingOccurrencesOfString:@"
    " withString:@"
    "];
        NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:text]; //  2 在 Core Text 中使用 NSAttributedString 而不是 NSString
        //为所有文本设置字体
        [attrString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:18] range:NSMakeRange(0, [attrString length])];
        //设置字体颜色
        //[attrString addAttribute:(NSString *)(kCTForegroundColorAttributeName) value:(id)[UIColor greenColor] range:NSMakeRange(0, [attrString length])]; //这个属性的文本的前景颜色。与该属性的值必须是一个CGColor对象。默认值是黑色的。
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString); //3  管理您的字体引用和文本绘制帧。
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                                                    CFRangeMake(0, [attrString length]), pathRef, NULL);
        
        CTFrameDraw(frame, context); //4  CTFrameDraw 将 frame 描述到设备上下文。
        
        CFRelease(frame); //5  最后,释放所有使用的对象。
        CFRelease(pathRef);
        CFRelease(framesetter);
    }

    绘制的时候是一条一条的绘制,比如一个选择题有一个问题和四个答案,那就要分5次绘制。

    下面贴上代码

    - (void)createPDF{
        
        //获取路径
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *saveDirectory = [paths firstObject];
        NSString *saveFileName = @"test.pdf";
        NSString *newFilePath = [saveDirectory stringByAppendingPathComponent:saveFileName];
        _pdfPath = newFilePath;
    //    if([[NSFileManager defaultManager] fileExistsAtPath:newFilePath]){
    //        [[NSFileManager defaultManager] removeItemAtPath:newFilePath error:nil];
    //    }
        NSLog(@"newFilePath:%@",newFilePath);
        const char *filename = [newFilePath UTF8String];
        
        //设置页面大小
        CGRect pageRect = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64);
        //1024 * 768
       // NSLog(@"pageRect w:%f  h:%f",pageRect.size.width, pageRect.size.height);
        
        //关联上下文对象
        CGContextRef pdfContext;
        CFStringRef path;
        CFURLRef url;
        
        path = CFStringCreateWithCString(NULL, filename, kCFStringEncodingUTF8);
        url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, 0);
        CFRelease(path);
        pdfContext = CGPDFContextCreateWithURL(url, &pageRect, nil);
        CFRelease(url);
    //    CGContextSetTextMatrix(pdfContext, CGAffineTransformIdentity); //它可以用来为每一个显示的字形单独设置变形矩阵。设置字形变换矩阵为CGAffineTransformIdentity,也就是说每一个字形都不做图形变换
        //转换Y轴坐标,  底层坐标与cocoa 组件不同 Y轴相反
    //    CGContextTranslateCTM(pdfContext, 0, CGRectGetHeight(self.view.bounds));
    //    CGContextScaleCTM(pdfContext, 1, -1);
        
        //NSString *text = @"11测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。
     测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。11222 english text";
        
    //    //以前的方法  drawAtPoint: 现在用Core Text替换了 可以试试drawInRect
    //    [@"hello world" drawAtPoint:CGPointMake(80, 80) withFont:[UIFont systemFontOfSize:18]];  //绘制汉字
        
        //设置一个Y轴上的偏移
        CGFloat y = 50;
        BOOL flag = YES;  //标记是否开始新的一页
        //需要画每个问题和答案选项
        for (int i = 0; i < _createArray.count; i++) {
            if (flag) {
                //开始绘制pdf 开始新一页
                CGContextBeginPage(pdfContext, &pageRect);
                //  画一个黑色的边框  页边距50
                CGContextStrokeRect(pdfContext, CGRectMake(40, 40, pageRect.size.width - 80, pageRect.size.height - 80));
                [self drawTextWithPageRect:pageRect andContext:pdfContext andText:@"成都****信息技术有限公司" byDrawInRect:CGRectMake(50, pageRect.size.height - 10, 240, -20)];
                //设置一个Y轴上的偏移
                y = 0;
                flag = NO;
            }
            Question *q = _createArray[i];
            //题目
            
            CGFloat strHeight = [self calulateHeightForString:q.question FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
            CGRect inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:[NSString stringWithFormat:@"%d.%@", i + 1, q.question] byDrawInRect:inRect];
            y += strHeight ;
            
            //选项 //pageRect.size.height - 50 - y
            strHeight = [self calulateHeightForString:q.item1 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
            inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item1  byDrawInRect:inRect];
            y += strHeight;
            
            strHeight = [self calulateHeightForString:q.item2 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
            inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item2  byDrawInRect:inRect];
            y += strHeight;
            
            strHeight = [self calulateHeightForString:q.item3 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
            inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item3  byDrawInRect:inRect];
            y += strHeight;
            
            
            strHeight = [self calulateHeightForString:q.item4 FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
            inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
            [self drawTextWithPageRect:pageRect andContext:pdfContext andText:q.item4  byDrawInRect:inRect];
            y += strHeight;
            if(!hideAnswer){  //判断是否显示答案
                strHeight = [self calulateHeightForString:q.answer FontType:[UIFont systemFontOfSize:18] RowWidth:pageRect.size.width - 100];
                inRect = CGRectMake(50, pageRect.size.height - 50 - y, pageRect.size.width - 100, -(strHeight + LINDESPACING));
                [self drawTextWithPageRect:pageRect andContext:pdfContext andText:[NSString stringWithFormat:@"答案:%@", q.answer] byDrawInRect:inRect];
                y += strHeight;
            }
            y = y + 5; //每个问题结束下面额外给5个点
            
            if ((pageRect.size.height - 50 - y - 40) < [self getHeightForQuestionCell:(i + 1)]) { //后面的40减去下边框的距离
                //结束当前页 开始新一页
                CGContextEndPage (pdfContext);
                flag = YES;
            }
    
        }
        
        //循环结束后,也要结束绘制最后一页
        CGContextEndPage (pdfContext);
        
        CGContextRelease (pdfContext);
        
        
    }

    上面获取路径部分,主要是获取PDF的上下文。绘制的时候,要注意,因为坐标系是左下为原点,往PDF绘文字时,文字虽然是正的,但是是往上面绘制的

    LINDESPACING 是行间距

    后面的if是判断剩下的距离能不能绘制下一题。

    解决办法:把要绘制的矩形区域的高度前面加负号“-”

    每一次绘制,都要开始新的一页

    CGContextBeginPage(pdfContext, &pageRect);

    与其对应的,在一页结束后,一定要结束一页 

    CGContextEndPage (pdfContext);

    之前就忘了写最后一个结束,导致出错,还找了好一会才找到BUG原因。

    里面还用到一个计算文字高度的方法,一起贴上吧,也方便以后万一忘了好查询。

    - (CGFloat)calulateHeightForString:(NSString *)str  FontType:(UIFont *)fontType RowWidth:(CGFloat) rowWidth{
        if (str==nil){
            return 0;
        }
        CGSize maxSize=CGSizeMake(rowWidth, 99999);
        //以前的方法
    //    CGSize  strSize=[str sizeWithFont:[UIFont systemFontOfSize:15]constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
     CGRect strSize = [str boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: fontType} context:nil]; 
    // [lrcStr sizeWithFont:[UIFont systemFontOfSize:self.fontSize] forWidth:rowWidth lineBreakMode:UILineBreakModeWordWrap]; //这个方法被抛弃了
    //return strSize.height;
    return strSize.size.height; }

    绘制出来的效果如下:

    以上内容由个人总结,不完善的地方,欢迎指正,共同学习。

  • 相关阅读:
    oracle 游标例子
    oracle 认识
    Scut游戏服务器引擎之新手入门
    Scut游戏服务器引擎6.5.8.6发布
    Scut游戏服务器引擎之Unity3d接入
    Scut游戏服务器引擎6.1.5.6发布,直接可运行,支持热更新
    ScutSDK 0.9版本发布
    Scut游戏服务器引擎6.0.5.2发布
    Scut游戏服务器引擎6.0.5.1发布
    Scut游戏服务器引擎6.0.5.0发布-支持C#脚本
  • 原文地址:https://www.cnblogs.com/endtel/p/4863064.html
Copyright © 2011-2022 走看看