美文网首页iOS开发
iOS端pdf预览实现(自己绘制)

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

作者: Lxyang | 来源:发表于2017-05-23 11:40 被阅读686次

    摘要


    由于项目需求,需要在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中太多方案了,于是只贴了以上几个文件代码,复制下来可直接使用。

    相关文章

      网友评论

        本文标题:iOS端pdf预览实现(自己绘制)

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