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