Custom Thumbnails and Previews with Quick Look on iOS
Learn how to build your own Quick Look preview and thumbnail extensions to display custom file types in iOS. By Chuck Krutsinger .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Custom Thumbnails and Previews with Quick Look on iOS
25 mins
- Getting Started
- Adding Quick Look Thumbnails
- Generating a Quick Look thumbnail
- Searching messages printed in the Console
- Defining and Exporting a Document Type
- Defining a Document Type
- Exporting a Document Type
- Quick Look Extensions
- Adding a Quick Look preview extension
- Invoking a Quick Look Preview Extension
- Adding a Thumbnail Extension
- Invoking a Thumbnail extension
- Trying Out Your Extensions Using the Files App
- Attaching the Debugger to Your Extension
- Where to Go From Here?
You may find yourself wanting to display a thumbnail representation of a file in your app, or you might even find yourself wanting to show a richer preview of the file itself. Fortunately, the QuickLook framework lets you generate thumbnails and display previews for many standard file types with little effort.
While this is great for standard file types, what if your app focuses on a specific type of file that isn’t covered by QuickLook? Well fear not; with Quick Look previews and thumbnail extension points introduced in iOS 13, you can provide custom previews and thumbnails for custom file types both in your own app and any other app on the same device that also uses QuickLook.
In this tutorial, you’ll learn how to:
- Generate thumbnail images using QLThumbnailGenerator
- Define and export your own document type for your custom file format
- Build your own Quick Look preview extension
- Build your own Thumbnail extension
- Debug your code that runs within an extension
To do this, you’ll work on RazeThumb, an app that shows a list of files and presents the Quick Look preview of each file when tapped. You’ll enhance the app to use QuickLook to display a thumbnail representing each file in the list. Finally, you’ll add a Quick Look preview extension and a Thumbnail extension for your custom .thumb file type and see it in action both in your app and in the Files app.
Getting Started
Begin by downloading the project materials using the Download Materials button at the top or bottom of this tutorial. Then open RazeThumb.xcodeproj in the starter folder.
RazeThumb is a simple document browsing app. It comes preloaded with a variety of file types containing thumb images. Five of the six formats are standard file types. The sixth is a custom file type called a .thumb file.
Build and run:
The app presents a list of six different file types, each represented with a placeholder document icon. Tapping each file will present the QLPreviewController
, which displays a preview of the file. That’s all it does for now, but you’ll be enhancing the app as you go.
Adding Quick Look Thumbnails
The first thing you’ll do is enhance RazeThumb to use a thumbnail of each file type. Currently, RazeThumb displays a generic document icon for each file. The QuickLook framework provides thumbnail images for a variety of file types, including images, PDFs, audio and video. Review Apple’s documentation for more details on what other file types Apple supports. If QuickLook doesn’t recognize a file type, it’ll still create some kind of placeholder thumbnail. You’ll learn more about that after you’ve added thumbnailing to the app.
For now, it’s time to start prettying up RazeThumb.
Open Document.swift and change import Foundation
to:
import QuickLook
At the bottom of the file, paste in the following extension:
// MARK: - QLThumbnailGenerator
extension Document {
func generateThumbnail(
size: CGSize,
scale: CGFloat,
completion: @escaping (UIImage) -> Void
) {
if let thumbnail = UIImage(systemName: "doc") {
completion(thumbnail)
}
}
}
The code above is a placeholder for your asynchronous thumbnail generating method. For now, it returns a UIImage
of the document system icon. In a moment, you’ll replace this with a request to the QuickLook framework to generate a thumbnail.
Next, open DocumentThumbnailView.swift and paste the following code immediately below .groupBoxStyle(PlainGroupBoxStyle())
:
.onAppear {
document.generateThumbnail(
size: thumbnailSize,
scale: displayScale
) { uiImage in
DispatchQueue.main.async {
self.thumbnail = Image(uiImage: uiImage)
}
}
}
The code above waits for the view to appear. Then, it asks for a thumbnail UIImage
for each file. Once the UIImage
arrives, the view updates the SwiftUI Image
using the main thread. This is necessary for all user interface updates.
Build and run:
You’ll see that the document icon for each file is now much smaller. This is because you didn’t size the images. But soon the QuickLook framework will handle the sizing for you.
Generating a Quick Look thumbnail
To get a thumbnail from the QuickLook framework, you’ll need to use QLThumbnailGenerator
to create a QLThumbnailGenerator.Request
and perform the request. Open Document.swift again and replace the contents of generateThumbnail(size:scale:completion:)
with the following code:
// 1
let request = QLThumbnailGenerator.Request(
fileAt: url,
size: size,
scale: scale,
representationTypes: .all)
// 2
let generator = QLThumbnailGenerator.shared
generator.generateRepresentations(for: request) { thumbnail, _, error in
// 3
if let thumbnail = thumbnail {
print("\(name) thumbnail generated")
completion(thumbnail.uiImage)
} else if let error = error {
print("\(name) - \(error)")
}
}
In the code above, you do the following:
- Create a new
QLThumbnailGenerator.Request
using the size and scale provided by the view, along with the defined representation type. The available representation types are.icon
,.lowQualityThumbnail
and.thumbnail
. In this case, you request all of them and use the best one. In cases where using all formats is too slow, you can specify one format. - Using the
.shared
generator, you start the request and wait for the thumbnails to be provided. - If the generator passes an image in the completion closure, you then pass it back to the view. If you receive an error, you print the error message. Keep in mind that the generator can return up to three different images because the request is for all representations.
Build and run:
You’ll now see the thumbnails for each file type. Unfortunately, QuickLook doesn’t know anything about .thumb files, so its thumbnail image is blank.
Searching messages printed in the Console
Now, take a look at the messages in the Debug area. There are three possible representations of each thumbnail, so there are three messages in the Console for each document displayed.
In the Filter field at the bottom-right corner of the Debug area, type .html. You’ll see there were three messages — one showing a thumbnail was generated and two showing failures. This is normal, and it’s something you can ignore when requesting all representations. The advantage of asking for all representations is that you’ll get at least one you can use.
Now erase .html and type .pdf. For the PDF file, QuickLook generated two thumbnails. The DocumentThumbnailView
displayed the placeholder document icon, and then it updated twice using the generated thumbnails. The trade-off for using all representations is the extra work of updating the view multiple times.
Replace .pdf and type .thumb. Even though QuickLook knows nothing about thumb files, it still gave you at least one thumbnail, albeit a blank one.
Now, take a moment to examine the different thumbnails the framework generated. The files zombiethumb.jpg, humanthumb.pdf and thumbsup.txt generated nice thumbnails of the file contents. The thumbnail of the Markdown file thumbsdown.md rendered as text since the framework doesn’t support Markdown rendering.
The thumbsdown.html file put up a generic HTML icon as its thumbnail, because the framework designers decided that rendering the HTML to produce an actual thumbnail wouldn’t perform well.
Finally, as you already discovered, the greenthumb.thumb thumbnail is a blank image. But don’t worry; you’ll soon fix that.
Give yourself a big “thumbs up” for generating thumbnails. On to the next thing.