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) {}
}
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
}
}