关于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:@"\r\n" withString:@"\n"]; 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测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。\n 测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。测试用一个很长长长长的文字去测试,要很长,看能不能换行什么的。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; }
绘制出来的效果如下:
以上内容由个人总结,不完善的地方,欢迎指正,共同学习。