QuickLook Previews for iOS: Getting Started

In this QuickLook Previews tutorial, you’ll learn how to integrate commonly supported file previews and editing capabilities into your iOS apps. By Renan Benatti Dias.

4.7 (9) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Conforming to QLPreviewItem

You may have noticed previewController(_:previewItemAt:) requires you to return a QLPreviewItem. QLPreviewController uses this protocol as an abstraction of your files. Conforming your data models to it enables QLPreviewController to preview them.

The protocol defines two required properties: previewItemURL and previewItemTitle. The first is a URL on disk — where the file is — and the second is the title QLPreviewController uses in the item navigation list.

Open File.swift and import QuickLook by adding the following line at the top of the file:

import QuickLook

Then, add this code at the end of the file:

// MARK: - QLPreviewItem
extension File: QLPreviewItem {
  var previewItemURL: URL? {
    url
  }
}

Conforming this class to QLPreviewItem lets you return an instance of File to the delegate method. If you don’t return a value to previewItemTitle, QLPreviewController uses the last path component of the URL as a title.

Note: NSURL already conforms to QLPreviewItem. If you don’t have a class representation of your files you may use an instance of NSURL as a QLPreviewItem.

Go back to ViewController.swift and replace the code inside previewController(_:previewItemAt:) with:

files[index]

Build and run. Open a spell to make sure everything is working.

Screen previewing PDF file of a Light Charm

The app continues to open tutorials as expected, but now all you have to do is return an instance of File for the selected index.

Generating Thumbnails for Each Document

The app looks pretty good so far, but it would be so much better if you could see a thumbnail of the document you want to preview. Fortunately, generating thumbnails is easy with the QuickLookThumbnailing framework.

Open File.swift and add this code below the QLPreviewItem class extension:

// MARK: - QuickLookThumbnailing
extension File {
  func generateThumbnail(completion: @escaping (UIImage) -> Void) {
    // 1
    let size = CGSize(width: 128, height: 102)
    let scale = UIScreen.main.scale
    // 2
    let request = QLThumbnailGenerator.Request(
      fileAt: url,
      size: size,
      scale: scale,
      representationTypes: .all)
    
    // 3
    let generator = QLThumbnailGenerator.shared
    generator.generateBestRepresentation(for: request) { thumbnail, error in
      if let thumbnail = thumbnail {
        completion(thumbnail.uiImage)
      } else if let error = error {
        // Handle error
        print(error)
      }
    }
  }
}

Here’s a breakdown of what’s happening:

  1. You define a size and scale. The framework uses them to generate a thumbnail of that size and scale.
  2. Then you create a thumbnail request using the file’s URL, size, scale and representation type.
  3. Finally you use QLThumbnailGenerator to generate the thumbnail for that request. By calling generateBestRepresentation(for:completion:), the framework generates the best possible thumbnail representation for the file or returns an error.

Open FileCell.swift and add the following at the end of update(with:):

file.generateThumbnail { [weak self] image in
  DispatchQueue.main.async {
    self?.thumbnailImageView.image = image
  }
}

This updates the thumbnail image on the main thread when the collection view loads. Build and run to see the results.

App home view with a collection of spell tutorial files represented with thumbnails

When dequeuing a cell, the file generates a thumbnail and assigns it to the cell’s UIImageView. Now you can see what you’re previewing even before opening the file. When QuickLook can’t generate a thumbnail, it generates an icon representation of the type.

Collection of spell files with thumbnails highlighting the icons

When generating high quality thumbnails for big files, QLThumbnailGenerator can take some time. Fortunately, there’s another method that can solve this: generateRepresentations(request:update:).

Open File.swift. Inside generateThumbnail(completion:), replace:

generator.generateBestRepresentation(for: request) { thumbnail, error in

With the following:

generator.generateRepresentations(for: request) { thumbnail, _, error in

This method quickly generates a file icon or a low-quality thumbnail and calls the updateHandler. Once a higher quality thumbnail is available, the framework calls the updateHandler again. If this thumbnail is available before the lower one, it may skip the first call entirely.

Build and run to make sure everything’s working fine.

App home view with a collection of spell tutorial files represented with thumbnails

Adding the Default Zoom Transition

Animations are an important part of iOS user interfaces. They give a polished feeling and sense of continuity to your app. Introduced in iOS 10, QuickLook provides an easy way to add animations with a modern method that does all the heavy lifting for you.

Open ViewController.swift and add a new property at the top of the class:

weak var tappedCell: FileCell?

Then, inside collectionView(_:didSelectItemAt:), add the following under quickLookViewController.dataSource = self:

tappedCell = collectionView.cellForItem(at: indexPath) as? FileCell
quickLookViewController.delegate = self

This code stores the tapped cell and sets the delegate property to self. But, since ViewController doesn’t conform to QLPreviewControllerDelegate, it can’t set self as the delegate. To fix this, create a class extension at the bottom of the file conforming to QLPreviewControllerDelegate:

// MARK: - QLPreviewControllerDelegate
extension ViewController: QLPreviewControllerDelegate {
  func previewController(
    _ controller: QLPreviewController,
    transitionViewFor item: QLPreviewItem
  ) -> UIView? {
    tappedCell?.thumbnailImageView
  }
}

By returning any UIView to previewController(_:transitionViewFor:), the framework creates a smooth zoom transition from this view to the preview. Here, you return the tapped cell’s UIImageView to create this transition.

When presenting or dismissing the preview, QuickLook uses the cell thumbnail to create a smooth zoom transition.

Build and run. Select a spell to see the transition in action.

Home screen opening a spell with a zoom in animation

QuickLook Editing Tools

iOS 13 brings users a cool new feature: simple editing support for images, PDFs and videos via QuickLook. This lets users preview and edit files from the QLPreviewController. The tools are the same ones you use when previewing a file in Files or editing an attachment in Mail.

QLPreviewController also provides simple support for trimming and rotating videos. This is quite handy when you need to take some notes on a PDF tutorial, for example.

Note: Bundle files are read only, meaning you cannot edit them unless you save a copy with the edits somewhere else. For simplicity, the app makes a copy of each file to its documents folder the first time it launches. The app uses those copies to display the collection of spells.

Enabling PDF Editing

In ViewController.swift, add the following code to the QLPreviewControllerDelegate extension at the bottom of the file:

func previewController(
  _ controller: QLPreviewController,
  editingModeFor previewItem: QLPreviewItem
) -> QLPreviewItemEditingMode {
  .updateContents
}

Here you return a value that indicates how the preview controller edits the file’s content. QuickLook handles editing by either overwriting the original file, .updateContent, or by creating an edited copy of it, .createCopy. If you don’t want the user to edit that file, you can return .disabled and QuickLook won’t show the edit button.

Build and run. Select the Light Charm tutorial and tap the new edit button at the top right. When a markup panel appears, highlight its light description with your favorite color.

QuickLook preview opening markup panel and highlighting light line yellow

QuickLook saves your changes in the original file, but you may notice those changes aren’t reflected on the thumbnail. That’s because you must generate a new thumbnail for that file with the edited file.

Home screen edited spell with old thumbnail

To fix this, update the UI after QuickLook saves the changes. Add this code inside the QLPreviewControllerDelegate extension block:

func previewController(
  _ controller: QLPreviewController,
  didUpdateContentsOf previewItem: QLPreviewItem
) {
  guard let file = previewItem as? File else { return }
  DispatchQueue.main.async {
    self.tappedCell?.update(with: file)
  }
}

QLPreviewController calls previewController(_:editUpdateContentsOf:) after the preview controller overwrites the contents of the original file. You can update the UI by reloading the contents of that cell.

When creating a copy for your edited file, QLPreviewController calls another method, previewController(_:didSaveEditedCopyOf:at:), when saving. Here, the framework gives you a temporary URL where the edited file is. You can use this URL to save the file anywhere you’d like in the app’s folders.

Build and run again. Make another edit on the file. When saving, the cell updates with a new thumbnail.

Collection of spells with the Light Charm thumbnail updated

Great job! This is the end of the tutorial, but that doesn’t mean you have to stop learning! Why don’t you learn some spells? Just be careful with dark magic. :]