macosswiftuipdfkitnsviewrepresentable

PDFThumbnailView overlaps the PDFView


I want to create a PDFView with embedded PDFThumbnailView for macOS. The PDFThumbnailView should be vertical, on the right side of the PDFView. I wish both views to be inside one NSViewRepresentable. However, I'm not very familiar with UIKit and its constraints and the best I could possibly do is to make the PDFThumbnailView overlap the PDFView. How to make the PDFView and the PDFThumbnailView separate, so they don't overlap? Here's the code:

struct PDFViewWrapper: NSViewRepresentable {

   let pdfView = PDFView()

   @Binding var pdfDisplay: Int
   
   func makeNSView(context: Context) -> PDFView  {
    
       let fileURL = FileManagerClient.shared.getDocumentsDirectory().appendingPathComponent("example.PDF")
    
       let pdfDocument = PDFDocument(url: fileURL)
    
    
    ///
       let thumbnailView = PDFThumbnailView()
       thumbnailView.translatesAutoresizingMaskIntoConstraints = false
       pdfView.addSubview(thumbnailView)
       thumbnailView.pdfView = pdfView
    
       NSLayoutConstraint.activate(
        [
            thumbnailView.leadingAnchor.constraint(equalTo: pdfView.safeAreaLayoutGuide.leadingAnchor),
            thumbnailView.topAnchor.constraint(equalTo: pdfView.safeAreaLayoutGuide.topAnchor),
            thumbnailView.bottomAnchor.constraint(equalTo: pdfView.safeAreaLayoutGuide.bottomAnchor),
            thumbnailView.widthAnchor.constraint(equalToConstant: 80),
            
        ]
    )

       thumbnailView.thumbnailSize = CGSize(width: 100, height: 100)
       thumbnailView.backgroundColor = .placeholderTextColor
    ///
    
    
       pdfView.document = pdfDocument
       pdfView.autoScales = true
       pdfView.displayMode = PDFDisplayMode(rawValue: pdfDisplay) ?? .singlePageContinuous
    
    
    
    
       return pdfView
   }
   
   func updateNSView(_ uiView: PDFView, context: Context) {}

}

screenshot


Solution

  • You can put the thumbnail view and the pdf view in a horizontal NSStackView.

    You can write a NSView subclass for that.

    class PdfViewWithThumbnails: NSView {
        let pdfView = PDFView()
        let pdfThumbnails = PDFThumbnailView()
        
        override init(frame frameRect: NSRect) {
            super.init(frame: frameRect)
            pdfThumbnails.thumbnailSize = CGSize(width: 100, height: 100)
            pdfThumbnails.backgroundColor = .placeholderTextColor
            pdfThumbnails.pdfView = pdfView
            pdfView.autoScales = true
            
            let stackView = NSStackView(views: [pdfThumbnails, pdfView])
            stackView.orientation = .horizontal
            addSubview(stackView)
            NSLayoutConstraint.activate([
                // fix the width of the thumbnail
                pdfThumbnails.widthAnchor.constraint(equalToConstant: 100),
                // pin the stack view to all four edges of PdfViewWithThumbnails
                stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
                stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
                stackView.topAnchor.constraint(equalTo: topAnchor),
                stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
            ])
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    struct PDFViewWrapper: NSViewRepresentable {
        
        let url: URL
        let pdfDisplay: PDFDisplayMode
    
         // you should not initialise the pdf view here. do it in makeNSView instead
         // otherwise this would cause a lot of needless allocations
         // every SwiftUI view update
        
        func makeNSView(context: Context) -> PdfViewWithThumbnails  {
            let pdfDocument = PDFDocument(url: url)
            let pdfWithThumbnails = PdfViewWithThumbnails()
            pdfWithThumbnails.pdfView.document = pdfDocument
            pdfWithThumbnails.pdfView.displayMode = pdfDisplay
            return pdfWithThumbnails
        }
        
        func updateNSView(_ nsview: PdfViewWithThumbnails, context: Context) {
            // this is where you update the AppKit views to respond to a SwiftUI view update
            if nsview.pdfView.document?.documentURL != url {
                nsview.pdfView.document = PDFDocument(url: url)
            }
            nsview.pdfView.displayMode = pdfDisplay
        }
    }