iOS 数据导出为PDF

需求:将列表数据导出为PDF

实现方式:

一、自己创建View,按照OC的方式画页面,画完之后将一页页View绘制到PDF文件中
优点:在View中画简单易懂,转成PDF的方式也简单
缺点:由于是将整个View给当成图片绘制到PDF中,保存的PDF内都是图片,无法修改文字,且!PDF文档非常的大!

二、从头到尾都使用手绘的方式去生成PDF,将各个控件自己画出来
优点:速度很快,生成的PDF文件足够的小,例:测试我的五万条数据,基本都是纯文本的,共计3000多页,需要15s左右,文件大小只有4M,如果使用第一种方式,五千条数据都300M了,差距有点大。
缺点:整个绘制过程比较麻烦,如果是统一样式的列表,还可以用for循环,如果特殊样式太多,全都要自己写。例:一行文本数据显示一行,最后超出的部分省略号表示,这个省略号都要自己写,并且要定义样式与前边的文字相同!

实现过程:
首先定义了页面的一些常用数据

static const CGFloat A4Width = 595.f; // PDF页面的宽
static const CGFloat A4Height = 842.f; // PDF页面的高
static const CGFloat topSpace = 40.f; // 页眉和页脚的高度
static const CGFloat bottomSpace = 50.f; // 页眉和页脚的高度 // 下边距需要留出来一定间距,不然会很挤
static const CGFloat leftRightSpace = 20.f; // 左右间距的宽度
static const CGFloat contentHeight = A4Height – topSpace – bottomSpace; // 除去页眉页脚之后的内容高度
static const CGFloat contentWidth = A4Width – leftRightSpace * 2; // 内容宽度
static const CGFloat targetSpace = 10.f; // 每个词条View的间距
static const CGFloat targetHeight = 14.f; // 词条信息每一行的高度
static const CGFloat favoritesHeight = 80.f; // 收藏夹的高度,也是收藏夹图片的高度

第一种实现方式:

// 创建数组存放PDF页面
NSMutableArray *viewArr = [[NSMutableArray alloc] init]; // 存放PDF的页面的数组

/*每个页面的绘制,自己去完成*/

// 关键代码,如何将页面绘制到PDF中, 需要特别注意的点:pdfName一定要带上.pdf的后缀名

+ (void)createPDFWithViewArr:(NSArray *)viewArr PDFName:(NSString *)pdfName { if (viewArr.count == 0 || pdfName.length == 0) return; NSMutableData *pdfData = [NSMutableData data]; // 文档信息 可设置为nil
CFMutableDictionaryRef myDictionary = CFDictionaryCreateMutable(nil, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(myDictionary, kCGPDFContextTitle, CFSTR(“PDF Ttitle”));
CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR(“PDF Author”)); // 设置PDF文件每页的尺寸
CGRect pageRect = CGRectMake(0, 0, A4Width, A4Height);
// PDF绘制尺寸,设置为CGRectZero则使用默认值612*912
UIGraphicsBeginPDFContextToData(pdfData, pageRect, nil); for (int i = 0; i < viewArr.count; i++) {
UIView *pageView = [viewArr objectAtIndex:i];
// PDF文档是分页的,开启一页文档开始绘制
UIGraphicsBeginPDFPage();
// 获取当前的上下文
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[pageView.layer renderInContext:pdfContext];
}
UIGraphicsEndPDFContext(); NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories objectAtIndex:0];
NSString *documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:pdfName];
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(@”documentDirectoryFileName: %@”,documentDirectoryFilename);
}
第一种方式是不是很简单? 对于内容不多,不需要编辑的PDF来说,完全可以使用这种方式。

第二种实现方式:

这里有几个点非常需要注意:

  • 在PDF的页面中,默认坐标原点是在左下角的,你可能需要对页面进行坐标系转换,而且是每个页面都需要先进行坐标系转换。
  • 手动绘制时页眉页脚页码这些都需要自己绘制,特别是页码,一定要想好怎么计算总共页面数
  • 释放对象,一定要释放你Create的所有对象,不然for循环次数太多,内容太大的时候,内存会崩掉的
  • 剩下的就是,文字的字体/颜色/大小/对齐,图片的圆角等等,需要查资料看下怎么设置的问题了

+ (void)toDrawPDFWithDataInfo:(MOJiPDFDataInfo *)dataInfo PDFName:(NSString *)pdfName { // 这里你可以根据内容计算一下总共需要多少页的PDF,后边for循环是根据页面来循环的
NSInteger allPageCount = 20; // 例如总共20页的PDF // 1.创建media box
CGFloat myPageWidth = A4Width;
CGFloat myPageHeight = A4Height;
CGRect mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight); // 2.设置pdf文档存储的路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
NSString *filePath = [documentsDirectory stringByAppendingFormat:@”/%@”, pdfName];
NSLog(@”%@”, filePath);
const char *cfilePath = [filePath UTF8String];
CFStringRef pathRef = CFStringCreateWithCString(NULL, cfilePath, kCFStringEncodingUTF8); // 3.设置当前pdf页面的属性
CFStringRef myKeys[3];
CFTypeRef myValues[3];
myKeys[0] = kCGPDFContextMediaBox;
myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
myKeys[1] = kCGPDFContextTitle;
myValues[1] = CFSTR(“我的PDF”);
myKeys[2] = kCGPDFContextCreator;
myValues[2] = CFSTR(“PDF作者”); // 4.获取pdf绘图上下文
CGContextRef myPDFContext = MyPDFContextCreate (&mediaBox, pathRef); // 设置字体样式
CTFontRef ctFontTitleRegular = CTFontCreateWithName(CFSTR(“PingFangSC-Regular”), 10.0, NULL);
// 设置字体颜色
CGColorRef ctColorBlack = [UIColor blackColor].CGColor; int t = 0; // 计数放这里是为了不在PDF页码循环时重置打断
for (int i = 0; i < allPageCount; i++) {
// 5.开始第i页页面
CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys, (const void **) myValues, 3,
&kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
CGPDFContextBeginPage(myPDFContext, pageDictionary); // 默认的原点在左下角,每一页都需要转换坐标系的操作!!!!! /* 这里写你每一页的绘制内容 */ CGPDFContextEndPage(myPDFContext); CFRelease(pageDictionary); } // 6.释放创建的对象
CGContextRelease(myPDFContext);
}

手动绘制,如何绘制图片/文字等比较麻烦,不过基本都可以在网上找到,至于每个函数的意义,可以参考苹果的官方文档。