Nick Marinov
Nick Marinov

Reputation: 148

CGPDFPage Drawing causing memory problems (Swift 3)

I'm currently developing an app and I want to implement a PDF reader on top of UICollectionView.

I am using a custom UIView on each cell which renders the corresponding PDF page:

weak var page: CGPDFPage! {

    didSet { setNeedsDisplay() }

}

override func draw(_ rect: CGRect) {

    if page == nil {
        print("page is nil")
        return }

    let context = UIGraphicsGetCurrentContext()

    context?.clear(bounds)

    context?.setFillColor(red: 1, green: 1, blue: 1, alpha: 1)
    context?.fill(bounds)

    context?.translateBy(x: 0.0, y: bounds.size.height);
    context?.scaleBy(x: 1.0, y: -1.0);

    var cropBox = page.getBoxRect(.cropBox)
    cropBox = CGRect(x: cropBox.origin.x, y: cropBox.origin.y, width: ceil(cropBox.width), height: ceil(cropBox.height))

    let scaleFactor = min(bounds.size.width/cropBox.size.width, bounds.size.height/cropBox.size.height)
    let scale = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
    let scaledInnerRect = cropBox.applying(scale)
    let translate = CGAffineTransform(translationX: ((bounds.size.width - scaledInnerRect.size.width) / 2) - scaledInnerRect.origin.x, y: ((bounds.size.height - scaledInnerRect.size.height) / 2) - scaledInnerRect.origin.y)
    let concat = translate.scaledBy(x: scaleFactor, y: scaleFactor)

    context?.concatenate(concat)

    let clipRect = cropBox
    context?.addRect(clipRect)
    context?.clip()

    context?.drawPDFPage(page)

    UIGraphicsEndPDFContext()

}

So far so good. It renders the page on the cell but it causes a memory problem.

enter image description here

Somehow the context keeps a reference to all the pages rendered on the cells of the collection view and they are not getting deallocated. CGPDFPageRelease is no longer available. :(

On the other hand, if this uiview subclass is zoomed inside a scrollview how can I redraw the pdf so I don't loose quality?

Any help will be highly appreciated. Thank you!

Upvotes: 2

Views: 1170

Answers (3)

Bogdan Evsenev
Bogdan Evsenev

Reputation: 881

I had same code and same issue, but once I changed it to this new solution, it helped me. Now I don't have any issues with memory while rendering PDF.

autoreleasepool is very important, since it help you release rendered pages. Here is my code.

SWIFT

class PDFPageRenderView: UIView {

    func render(page: CGPDFPage) {
        for subview in subviews { subview.removeFromSuperview() }
        let imageView = UIImageView(frame: bounds)
        imageView.contentMode = .scaleAspectFit
        imageView.clipsToBounds = true
        imageView.backgroundColor = UIColor.white
        autoreleasepool {
            let pageRect = page.getBoxRect(.mediaBox)
            let renderer = UIGraphicsImageRenderer(size: pageRect.size)
            let data =  renderer.jpegData(withCompressionQuality: kImageCompression, actions: { cnv in
                UIColor.white.set()
                cnv.fill(pageRect)
                cnv.cgContext.translateBy(x: 0.0, y: pageRect.size.height)
                cnv.cgContext.scaleBy(x: 1.0, y: -1.0)
                cnv.cgContext.drawPDFPage(page)
            })
            imageView.image = UIImage(data: data)
        }
        addSubview(imageView)
    }
}

Upvotes: 6

Martin
Martin

Reputation: 866

Use CATiledLayer to solve rendering problem on page zoom.

Create a subclass of CATiledLayer and override below given method in your view subclass

override class var layerClass: AnyClass {
        get {
            return YourCATiledLayerSubclass.self
        }
    }

Upvotes: 0

luke991
luke991

Reputation: 51

We were seeing the same leak and it only seems to affect iOS 10. It seems to be a common issue: https://forums.developer.apple.com/message/174710#174710 We have also tested it in iOS 10.2 and cannot find a leak anymore. You have you tried to see if that works for you?

Upvotes: 0

Related Questions