iOS端pdf预览实现(自己绘制)

摘要


由于项目需求,需要在app里实现合同pdf文件供给客户预览,总结了几种实现方案:

  • 1、使用UIWebView,由于是后台返回pdf路径,我们直接加载,那么pdf预览是不支持捏合放大缩小(下载下来是可以做到)。ps:不下载,直接加载,偿试了多次均不可放大缩小。
  • 2、使用WKWebView,可实现放大缩小等功能。
  • 3、自己手写绘制。同WKWebView。

以上三种方案,均达不到项目需求,合同里面的公司盖章与手写签名预览不了。原因:遵从了ADOBE的业务规则,苹果自己的浏览器及其他浏览器可能不支持这些规则,导致不能看到签章。要么用adobe的官方阅读器展示PDF,要么使用合同生成厂商的PDF-SDK来预览。

由于偿试了以上三种方案均没有显示出签章,仅以此文作笔记供大家参考。
本文主要讲述自己手写实现pdf预览。

主体思路


1.使用CGPDFDocument读取远端pdf资料
2.使用UICollectionView控制显示读取到的资料
3.本文代码均使用swift编写。

(代码注释很详细)

代码相关


代码组成

  • XYJPdfReaderView.swift ------pdf绘制板
  • XYJPdfDocument.swift ------pdf文件读取
  • XYJPDFCollectionViewCell.swift ------预览cell
  • XYJPDFReaderVC.swift ------预览控制器
简单使用
let url = "https://test.*****.com/customerFile/audit/20**3014932***5461**借款协议Z120170427170341374.pdf"
let vc = XYJPDFReaderVC.init()
vc.url = url
self.navigationController?.pushViewController(vc, animated: true)
各文件代码

XYJPdfDocument.swift

import UIKit

class XYJPdfDocument: NSObject {
    
    
    /// 根据url字符串生成pdf数据
    ///
    /// - Parameter urlString: pdf的路径
    /// - Returns: pdf数据
    static func getPdfDocumentRef(urlString: String) -> CGPDFDocument? {
        
        /// url字符串转成URL类型
        let str = urlString.addingPercentEscapes(using: String.Encoding.utf8) ?? ""
        guard let url = URL.init(string: str) else { return nil }
        
        /// 通过url获取文件内容
        let pdfData = try? Data.init(contentsOf: url, options: Data.ReadingOptions.init(rawValue: 0)) as CFData
        var proRef: CGDataProvider?
        if pdfData != nil {
            proRef = CGDataProvider.init(data: pdfData!)
        }
        if proRef != nil {
            let documentRefDocu = CGPDFDocument.init(proRef!)
            if documentRefDocu != nil {
                return documentRefDocu
            } else {
                return nil
            }
        } else {
            return nil
        }
    }
}

XYJPdfReaderView.swift

import UIKit

class XYJPdfReaderView: UIView {

    /// pdf数据
    var documentRef: CGPDFDocument?
    /// 页数
    var pageNum = 0
    
    /// 创建一个视图
    ///
    /// - Parameters:
    ///   - frame: 尺寸
    ///   - documentRef: pdf数据
    ///   - pageNum: 当前页码数
    /// - Returns: 视图
    init(frame: CGRect, documentRef: CGPDFDocument?, pageNum: Int) {
        super.init(frame: frame)
        self.documentRef = documentRef
        self.pageNum = pageNum
        self.backgroundColor = UIColor.white
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// 重绘
    ///
    /// - Parameter rect: 尺寸
    override func draw(_ rect: CGRect) {
        
        self.drawPdfIn(context: UIGraphicsGetCurrentContext()!)
    }
    
    func drawPdfIn(context: CGContext) -> Void {
        
        // 调整位置
        context.translateBy(x: 0.0, y: self.frame.size.height)
        // 使图形呈正立显示
        context.scaleBy(x: 1.0, y: -1.0)
        // 获取需要绘制该页码的数据
        let pageRef = self.documentRef?.page(at: self.pageNum+1)
        // 创建一个仿射变换的参数给函数
        let pdfTransform = pageRef?.getDrawingTransform(CGPDFBox.cropBox, rect: self.bounds, rotate: 0, preserveAspectRatio: true)
        // 把创建的仿射变换参数和上下文环境联系起来
        context.concatenate(pdfTransform!)
        // 把得到的指定页的PDF数据绘制到视图上
        context.drawPDFPage(pageRef!)   
    }
}

XYJPDFReaderVC.swift

import UIKit

class XYJPDFReaderVC: UIViewController {

    /// 要显示的pdf文档地址
    var url: String? {
        didSet {
            if url != nil{
                docRef = XYJPdfDocument.getPdfDocumentRef(urlString: url!)
            }
        }
    }
    
    /// 要显示的pdf文档
    private var docRef: CGPDFDocument? {
        didSet {
            totalPage = docRef?.numberOfPages
        }
    }
    
    /// 存数据的数组
    fileprivate var dataArray: Array<XYJPdfReaderView>? {
        get {
            var array = [XYJPdfReaderView]()
            for i in 0..<(totalPage ?? 0) {
                let pdfV = XYJPdfReaderView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height), documentRef: docRef, pageNum: i)
                array.append(pdfV)
            }
            return array
        }
    }
    
    /// 一共多少页
    private var totalPage: Int?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.white
        configePage()
        
    }


    /// 配置页面
    func configePage() -> Void {
        view.addSubview(collectionView)
    }
    
    
    
    /// 用于显示的collectionView
    lazy var collectionView: UICollectionView = {
        
        let layout = UICollectionViewFlowLayout.init()
        layout.itemSize = self.view.frame.size
        layout.scrollDirection = .vertical
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        
        let collectionV = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
        collectionV.isPagingEnabled = true
        collectionV.register(XYJPDFCollectionViewCell.self, forCellWithReuseIdentifier: "XYJPDFCollectionViewCell")
        collectionV.dataSource = self
        collectionV.delegate = self
        collectionV.backgroundColor = UIColor.white

        return collectionV
    }()
}


extension XYJPDFReaderVC: UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.dataArray?.count ?? 0
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "XYJPDFCollectionViewCell", for: indexPath) as! XYJPDFCollectionViewCell
        cell.showView = self.dataArray?[indexPath.item]
        return cell
    }
    
}

extension XYJPDFReaderVC: UICollectionViewDelegate {

}

extension XYJPDFReaderVC: UIScrollViewDelegate {
    
    /// 当某个item不在当前视图中显示的时候,将它的缩放比例还原
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        for view in scrollView.subviews {
            if view is XYJPDFCollectionViewCell {
                (view as! XYJPDFCollectionViewCell).contentScrollV.zoomScale = 1.0
            }
        }
    }
    
}

XYJPDFCollectionViewCell.swift

import UIKit

class XYJPDFCollectionViewCell: UICollectionViewCell,UIScrollViewDelegate {
    
    /// 用于显示pdf内容的视图
    var showView: XYJPdfReaderView? {
        didSet {
            for view in contentScrollV.subviews {
                view.removeFromSuperview()
            }
            contentScrollV.addSubview(showView!)
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(contentScrollV)
    }
    /// 用于实现缩放功能的UISCrollView
    lazy var contentScrollV: UIScrollView = {
        let contentV = UIScrollView.init(frame: self.bounds)
        contentV.contentSize = self.frame.size
        contentV.minimumZoomScale = 0.5
        contentV.maximumZoomScale = 2.5
        contentV.delegate = self
        return contentV
    }()
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: 代理方法
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        
        for view in contentScrollV.subviews {
            if view is XYJPdfReaderView {
                return view
            }
        }
        return nil
        
    }
}

写在最后

本打算把demo放到github上,但是demo中太多方案了,于是只贴了以上几个文件代码,复制下来可直接使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 圣诞快乐\^O^/ 好希望下一场大雪,牵着你的手,咯吱咯吱的踩在雪上,一起到白头。 这是我心中最美的画面。 一起去...
    南容555阅读 156评论 0 0
  • 1 Does China play fair? Tensions on China's industrial mi...
    吴筱雨_8dbf阅读 177评论 0 0
  • 有一丝温暖袭上心头 但我却有一丝惶恐 唯不能以一颗全心别人 想来我是一个渴望真诚却又害怕过于接近的人
    led护眼灯阅读 172评论 0 0
  • 情书群通关100篇,这封情书想要写给你~东东。 我跳了这么多坑,全都要感谢你! 如果不是因为你,我就不会认识周洋,...
    常拓阅读 356评论 1 18