TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

How to set the Anchor Points of a PDF in PDFKit?

Forums > SwiftUI

I am trying to set the top anchor point of a pdf that is inside of a view with the .ignoresSafeArea() modifier. I would also like it to work on the edges when the phone is in landscape although for simplicity I will only explain what I want for the top. I want it to function like the iOS Files app pdf viewer where when tapped it hides the navigation bars but the top of the pdf stays at the same place, but when you zoom in on the pdf it can fill the whole screen. When you zoom back out the top should return to the same place as before. Here is a simple view to show how it is being used:

@MainActor
struct ContentView: View {
    @State var showBars: Bool = true
    @State var pdfUrl: URL?
    var body: some View {
        NavigationStack {
            GeometryReader { geo in
                ScrollView {
                    TabView {
                        if let url = pdfUrl {
                            PDFViewer(pdfUrl: url)
                                .onTapGesture {
                                    withAnimation {
                                        showBars.toggle()
                                    }
                                }
                        }
                    }
                    .tabViewStyle(.page(indexDisplayMode: .never))
                    .frame(width: geo.size.width, height: geo.size.height)
                }
                .scrollDisabled(true)
            }
            .ignoresSafeArea(edges: !showBars ? .all : [])
        }
        .task {
            pdfUrl = renderPDF()
        }
    }

    func renderPDF() -> URL {
        let renderer = ImageRenderer(content: VStack {})

        let url = URL.documentsDirectory.appending(path: "samplepdf.pdf")

        renderer.render { size, context in

            guard let pdf = CGContext(url as CFURL, mediaBox: nil, nil) else {
                return
            }

            pdf.beginPDFPage(nil)
            context(pdf)
            pdf.endPDFPage()
            pdf.beginPDFPage(nil)
            context(pdf)

            pdf.endPDFPage()
            pdf.closePDF()
        }

        return url
    }
}

And here is what my pdfView looks like so far:

struct PDFViewer: View {
    var pdfUrl: URL
    var body: some View {
        PDFSheetView(document: .init(url: pdfUrl))
    }
}

class PDFViewController: UIViewController {
    let document: PDFDocument?
    var pdfView: PDFView!

    init(document: PDFDocument?) {
        self.document = document

        super.init(nibName: nil, bundle: nil)
    }

    override func loadView() {
        let view = PDFView()
        self.view = view
        self.pdfView = view

        view.document = document
        view.displayDirection = .vertical
        view.autoScales = true
        view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
        view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
    }

    required init?(coder: NSCoder) {
        document = nil

        super.init(coder: coder)
        return nil
    }

    override func viewDidLayoutSubviews() {
        let bounds = view.bounds
        if let document {
            if let page = document.page(at: 0) {
                let pageBounds = page.bounds(for: .mediaBox)
                if bounds.width > 0 && pageBounds.width > 0 {
                    let scaleFactor = bounds.width / pageBounds.width
                    let subtractionFactor = scaleFactor * 0.0125
                    pdfView.minScaleFactor = scaleFactor - subtractionFactor
                }
            }
        }
    }
}

struct PDFSheetView: UIViewControllerRepresentable {
    typealias UIViewControllerType = PDFViewController

    let document: PDFDocument?

    func makeUIViewController(context: Context) -> PDFViewController {

        let controller = PDFViewController(document: document)

        return controller
    }

    func updateUIViewController(_ uiViewController: PDFViewController, context: Context) {

    }
}

Is this possible to do? Like I said before, I want it to function just like the iOS Files app pdf viewer.

3      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.