Reputation:
I'm working on an app that displays a PDF using PDFKit, and I need to be able to set the minimum zoom level - otherwise the user can just zoom out forever. I've tried to set minScaleFactor
and maxScaleFactor
, and because these turn off autoScales
, I need to set the scaleFactor
to pdfView.scaleFactorForSizeToFit
. However, this setting doesn't result in the same beginning zoom as autoScales
and despite changing the actual scaleFactor
number, the beginning zoom doesn't change. This photo is with autoScales
on:
[![image with autoscales on][1]][1]
and then what happens when I use the scaleFactorForSizeToFit
:
[![image with scaleFactorForSizeToFit][2]][2]
To quote the apple documentation for scaleFactorForSizeToFit
, this is the
"size to fit" scale factor that autoScales would use for scaling the current document and layout.
I've pasted my code below. Thank you for your help.
import PDFKit
import SwiftUI
import Combine
class DataLoader : ObservableObject {
@Published var data : Data?
var cancellable : AnyCancellable?
func loadUrl(url: URL) {
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.receive(on: RunLoop.main)
.sink(receiveCompletion: { (completion) in
switch completion {
case .failure(let failureType):
print(failureType)
//handle potential errors here
case .finished:
break
}
}, receiveValue: { (data) in
self.data = data
})
}
}
struct PDFSwiftUIView : View {
@StateObject private var dataLoader = DataLoader()
var StringToBeLoaded: String
var body: some View {
VStack {
if let data = dataLoader.data {
PDFRepresentedView(data: data)
.navigationBarHidden(false)
} else {
CustomProgressView()
//.navigationBarHidden(true)
}
}.onAppear {
dataLoader.loadUrl(url: URL(string: StringToBeLoaded)!)
}
}
}
struct PDFRepresentedView: UIViewRepresentable {
typealias UIViewType = PDFView
let data: Data
let singlePage: Bool = false
func makeUIView(context _: UIViewRepresentableContext<PDFRepresentedView>) -> UIViewType {
let pdfView = PDFView()
// pdfView.autoScales = true
// pdfView.maxScaleFactor = 0.1
pdfView.minScaleFactor = 1
pdfView.scaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.maxScaleFactor = 10
if singlePage {
pdfView.displayMode = .singlePage
}
return pdfView
}
func updateUIView(_ pdfView: UIViewType, context: UIViewRepresentableContext<PDFRepresentedView>) {
pdfView.document = PDFDocument(data: data)
}
func canZoomIn() -> Bool {
return false
}
}
struct ContentV_Previews: PreviewProvider {
static var previews: some View {
PDFSwiftUIView(StringToBeLoaded: "EXAMPLE_STRING")
.previewInterfaceOrientation(.portrait)
}
}
Upvotes: 2
Views: 1284
Reputation: 527
This last solution works but you are kind of hardcoding the scale factor. so it only works if the page height is always the same. And bear in mind that macOS does not have a UIScreen and even under iPadOS there can be several windows and the window can have a different height than the screen height with the new StageManager... this worked for me:
first, wrap your swiftUIView (in the above example PDFRepresentedView) in a geometry reader. pass the view height (proxy.size.height) into the PDFRepresentedView.
in makeUIView, set
pdfView.maxScaleFactor = some value > 1
pdfView.autoScales = true
in updateUiView, set:
let pdfView = uiView
if let pageHeight = pdfView.currentPage?.bounds(for: .mediaBox).height {
let scaleFactor:CGFloat = self.viewHeight / pageHeight
pdfView.minScaleFactor = scaleFactor
}
Since my app also support macOS, I have written an NSViewRepresentable in the same way. Happy coding!
Upvotes: 1
Reputation:
I was eventually able to solve this. The following code is how I managed to solve it:
if let document = PDFDocument(data: data) {
pdfView.displayDirection = .vertical
pdfView.autoScales = true
pdfView.document = document
pdfView.setNeedsLayout()
pdfView.layoutIfNeeded()
pdfView.minScaleFactor = UIScreen.main.bounds.height * 0.00075
pdfView.maxScaleFactor = 5.0
}
For some reason, the pdfView.scaleFactorForSizeToFit
doesn't work - it always returns 0. This might be an iOS 15 issue - I noticed in another answer that someone else had the same issue. In the code above, what I did was I just scaled the PDF to fit the screen by screen height. This allowed me to more or less "autoscale" on my own. The code above autoscales the PDF correctly and prevents the user from zooming out too far.
Upvotes: 1
Reputation: 36264
maybe it is to do with the sequence. This seems to work for me:
pdfView.scaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.maxScaleFactor = 10.0
pdfView.minScaleFactor = 1.0
pdfView.autoScales = true
Upvotes: 1