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中太多方案了,于是只貼了以上幾個文件代碼,復制下來可直接使用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,241評論 4 61
  • 圣誕快樂\^O^/ 好希望下一場大雪,牽著你的手,咯吱咯吱的踩在雪上,一起到白頭。 這是我心中最美的畫面。 一起去...
    南容555閱讀 166評論 0 0
  • 1 Does China play fair? Tensions on China's industrial mi...
    吳筱雨_8dbf閱讀 185評論 0 0
  • 有一絲溫暖襲上心頭 但我卻有一絲惶恐 唯不能以一顆全心別人 想來我是一個渴望真誠卻又害怕過于接近的人
    led護眼燈閱讀 179評論 0 0
  • 情書群通關100篇,這封情書想要寫給你~東東。 我跳了這么多坑,全都要感謝你! 如果不是因為你,我就不會認識周洋,...
    常拓閱讀 371評論 1 18