Skip to content

Instantly share code, notes, and snippets.

@stevelandeyasana
Created February 10, 2020 23:16
Show Gist options
  • Save stevelandeyasana/b50d507a6ba822372fad224ac9639b7d to your computer and use it in GitHub Desktop.
Save stevelandeyasana/b50d507a6ba822372fad224ac9639b7d to your computer and use it in GitHub Desktop.
A UIView that renders a PDF page at a given resolution and constrains itself to the PDF page's aspect ratio
// Uses Cartography for layout, but you should be able to translate it to NSLayoutConstraint pretty easily
import Cartography
/// Render a single page of a PDF, non-interactive, at an arbitrary size.
/// Adds its own constraint to maintain its correct aspect ratio.
class PDFPageView: UIView {
var displayBox: PDFDisplayBox = .artBox
var majorAxisLength: CGFloat = 2048
var page: PDFPage? { didSet { applyState() } }
private let aspectRatioConstrainGroup = ConstraintGroup()
convenience init(page: PDFPage?) {
self.init(frame: .zero)
self.page = page
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func applyState() {
if let page = page {
let bounds = page.bounds(for: displayBox)
let multiplier = bounds.size.width / bounds.size.height
constrain(self, replace: aspectRatioConstrainGroup) { it in
it.width == it.height * multiplier
}
setNeedsDisplay()
setNeedsLayout()
} else {
// no constraints at all (no PDF, so no 'correct' aspect ratio)
constrain(self, replace: aspectRatioConstrainGroup) { _ in }
}
}
// MARK: UIView
override var intrinsicContentSize: CGSize {
if let page = page {
let bounds = page.bounds(for: displayBox)
if bounds.size.width > bounds.size.height {
return CGSize(
width: majorAxisLength,
height: majorAxisLength * (bounds.size.height / bounds.size.width))
} else {
return CGSize(
width: majorAxisLength * (bounds.size.width / bounds.size.height),
height: majorAxisLength)
}
} else {
return .zero
}
}
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext(), let page = page else { return }
// Render the PDF to the CGContext.
// PDF coordinate system is Y-flipped from Core Graphics
context.translateBy(x: 0, y: rect.height)
// Ask PDF to apply its crop box transform
let bounds = page.bounds(for: displayBox)
page.transform(context, for: displayBox)
// Scale PDF to view size
context.scaleBy(x: rect.width / bounds.width, y: -rect.height / bounds.height)
// Draw!
page.draw(with: displayBox, to: context)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment