Sticky Header for PDF Thumbnail View in Swift for iOS

Set up a sticky and customized header for the document thumbnail scroll view. Get additional resources by visiting our PSPDFThumbnailFlowLayout API guide.


//
// Copyright © 2021-2025 PSPDFKit GmbH. All rights reserved.
//
// The Nutrient sample applications are licensed with a modified BSD license.
// Please see License for details. This notice may not be removed from this file.
//
import PSPDFKit
import PSPDFKitUI
final class StickyHeaderExample: Example {
override init() {
super.init()
title = "Sticky Thumbnail Header"
contentDescription = "Shows how to set up a sticky and customized header in thumbnail mode."
category = .subclassing
priority = 10
}
override func invoke(with delegate: ExampleRunnerDelegate?) -> UIViewController {
return StickyHeaderViewController(document: AssetLoader.document(for: .annualReport))
}
}
private class StickyHeaderViewController: PDFViewController {
override func viewDidLoad() {
super.viewDidLoad()
// First, enable the sticky header:
if let layout = thumbnailController.collectionViewLayout as? ThumbnailFlowLayout {
layout.stickyHeaderEnabled = true
}
// By default, the header view does not have a visible background.
// This looks good when it scrolls along with the page thumbnails, but it looks terrible if you use the sticky header.
// Because we only want to customize the header in this example, leaving the other samples untouched, we use `appearance(whenContainedInInstancesOf:)`.
// In a typical app you would probably just use `appearance()`.
let headerAppearance = CollectionReusableFilterView.appearance(whenContainedInInstancesOf: [StickyHeaderViewController.self])
// For this app, a dark translucent background looks good.
headerAppearance.backgroundStyle = CollectionReusableFilterView.Style.darkBlur
// If that’s visually just “too much” for your app, you can tone it down by simply setting a background color instead:
// headerAppearance.backgroundColor = .darkText
// The filterElement is centered inside the header, but we could apply an offset if we wanted to:
// headerAppearance.filterElementOffset = CGPoint(x: 0, y: 200)
// Well that would obviously be silly!
// If you comment the above line in, note that the filter does not extend beyound the header’s bounds.
// In fact, there even is a minimum margin.
// Let’s say we want that minimum margin to be 0 in X and two times the default in Y, so that the filterSegment shrinks noticably:
var filterMargin: UIEdgeInsets = .zero
filterMargin.top = 2 * PSPDFCollectionReusableFilterViewDefaultMargin
filterMargin.bottom = filterMargin.top
headerAppearance.minimumFilterMargin = filterMargin
// And of course, we can also style the segmented control:
let filterAppearance = UISegmentedControl.appearance(whenContainedInInstancesOf: [CollectionReusableFilterView.self, StickyHeaderViewController.self])
if let font = UIFont(name: "Avenir", size: 12) {
filterAppearance.setTitleTextAttributes([.font: font], for: .normal)
}
if let font = UIFont(name: "Avenir-Black", size: 12) {
filterAppearance.setTitleTextAttributes([.font: font], for: .selected)
}
// That’s it!
// If you need further customizations for the header — like inserting additional views — you do have to subclass `ThumbnailViewController`.
// Methods to override there are (in descending order of probabbility):
// 1. `collectionView(_:layout:referenceSizeForHeaderInSection:)` if you want to adjust the header height
// 2. `collectionView(_:viewForSupplementaryElementOfKind:at:)` if you want to insert additional views or constraints into the header
}
}

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.