Communitygithub.com

JordanCoin/ios-skills-collection – PDFKit

Afficher et manipuler des documents PDF avec PDFKit. À utiliser pour intégrer PDFView, créer ou modifier des instances PDFDocument, ajouter des annotations (surlignements, notes, signatures), extraire du texte avec PDFSelection, naviguer entre les pages, générer des vignettes, remplir des formulaires PDF ou encapsuler PDFView dans SwiftUI.

Qu'est-ce que ios-skills-collection – PDFKit ?

Cette compétence fournit un guide complet et prêt à l'emploi pour maîtriser PDFKit sur iOS, iPadOS, macOS et visionOS. Elle couvre toutes les fonctionnalités essentielles : affichage de PDF avec PDFView (modes simple page, continu, deux pages), chargement de documents à partir d'URL, de Data ou création de documents vierges, navigation par page ou par destination, et recherche de texte synchrone et asynchrone via PDFSelection et PDFDocumentDelegate. L'accent est mis sur la manipulation avancée des annotations (surlignements, notes collantes, textes libres, liens, formes) avec des exemples concrets, y compris la gestion du système de coordonnées PDF (origine en bas à gauche) et la sécurité des threads. La compétence inclut également l'intégration SwiftUI via UIViewRepresentable, la génération de vignettes avec PDFThumbnailView, les superpositions de pages pour iOS 16+, et une checklist de révision pour éviter les erreurs courantes comme le force-unwrap de PDFDocument ou l'oubli de autoScales. Elle est conçue pour les développeurs utilisant Swift 6.3 / iOS 26+ et des agents comme Claude Code, Cursor ou Codex, offrant une référence instantanée pour toute tâche de manipulation de PDF.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/JordanCoin/ios-skills-collection/tree/HEAD/skills/dpearson2699--pdfkit

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

PDFKit

Affichez, naviguez, recherchez, annotez et manipulez des documents PDF avec PDFView, PDFDocument, PDFPage, PDFAnnotation et PDFSelection. Cible Swift 6.3 / iOS 26+.

Table des matières

Configuration

PDFKit ne nécessite ni entitlements ni entrées Info.plist.

import PDFKit

Disponibilité des plateformes : iOS 11+, iPadOS 11+, Mac Catalyst 13.1+, macOS 10.4+, tvOS 11+, visionOS 1.0+.

Affichage des PDF

PDFView est une sous-classe de UIView qui affiche le contenu PDF, gère le zoom, le défilement, la sélection de texte et la navigation entre les pages prêts à l'emploi.

import PDFKit
import UIKit

class PDFViewController: UIViewController {
    let pdfView = PDFView()

    override func viewDidLoad() {
        super.viewDidLoad()
        pdfView.frame = view.bounds
        pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(pdfView)

        pdfView.autoScales = true
        pdfView.displayMode = .singlePageContinuous
        pdfView.displayDirection = .vertical

        if let url = Bundle.main.url(forResource: "sample", withExtension: "pdf") {
            pdfView.document = PDFDocument(url: url)
        }
    }
}

Modes d'affichage

ModeComportement
.singlePageUne seule page à la fois
.singlePageContinuousPages empilées verticalement, défilables
.twoUpDeux pages côte à côte
.twoUpContinuousDeux pages avec défilement continu

Mise à l'échelle et apparence

pdfView.autoScales = true
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.maxScaleFactor = 4.0

pdfView.displaysPageBreaks = true
pdfView.pageShadowsEnabled = true
pdfView.interpolationQuality = .high

Chargement de documents

PDFDocument se charge à partir d'une URL, de Data, ou peut être créé vide.

let fileDoc = PDFDocument(url: fileURL)
let dataDoc = PDFDocument(data: pdfData)
let emptyDoc = PDFDocument()

PDF protégés par mot de passe

guard let document = PDFDocument(url: url) else { return }
if document.isLocked {
    if !document.unlock(withPassword: userPassword) {
        // Afficher la demande de mot de passe
    }
}

Sauvegarde et manipulation de pages

document.write(to: outputURL)
document.write(to: outputURL, withOptions: [
    .ownerPasswordOption: "ownerPass", .userPasswordOption: "userPass"
])
let data = document.dataRepresentation()

// Pages (indexées à partir de 0)
let count = document.pageCount
document.insert(PDFPage(), at: count)
document.removePage(at: 2)
document.exchangePage(at: 0, withPageAt: 3)

Navigation entre les pages

PDFView fournit une navigation intégrée avec suivi de l'historique.

// Aller à une page spécifique
if let page = pdfView.document?.page(at: 5) {
    pdfView.go(to: page)
}

// Navigation séquentielle
pdfView.goToNextPage(nil)
pdfView.goToPreviousPage(nil)
pdfView.goToFirstPage(nil)
pdfView.goToLastPage(nil)

// Vérifier l'état de la navigation
if pdfView.canGoToNextPage { /* ... */ }

// Navigation dans l'historique
if pdfView.canGoBack { pdfView.goBack(nil) }

// Aller à un point spécifique sur une page
let destination = PDFDestination(page: page, at: CGPoint(x: 0, y: 500))
pdfView.go(to: destination)

Observation des changements de page

NotificationCenter.default.addObserver(
    self, selector: #selector(pageChanged),
    name: .PDFViewPageChanged, object: pdfView
)

@objc func pageChanged(_ notification: Notification) {
    guard let page = pdfView.currentPage,
          let doc = pdfView.document else { return }
    let index = doc.index(for: page)
    pageLabel.text = "Page \(index + 1) sur \(doc.pageCount)"
}

Recherche et sélection de texte

Recherche synchrone

let results: [PDFSelection] = document.findString(
    "terme de recherche", withOptions: [.caseInsensitive]
)

Recherche asynchrone

Utilisez PDFDocumentDelegate pour les recherches en arrière-plan sur de grands documents. Implémentez didMatchString(_:) pour recevoir chaque correspondance et documentDidEndDocumentFind(_:) pour la fin.

Recherche incrémentale et interaction de recherche

// Correspondance suivante à partir de la sélection courante
let next = document.findString("terme", fromSelection: current, withOptions: [.caseInsensitive])

// Barre de recherche système (iOS 16+)
pdfView.isFindInteractionEnabled = true

Extraction de texte

let fullText = document.string                          // Document entier
let pageText = document.page(at: 0)?.string             // Page unique
let attributed = document.page(at: 0)?.attributedString  // Avec mise en forme

// Extraction par région
if let page = document.page(at: 0) {
    let selection = page.selection(for: CGRect(x: 50, y: 50, width: 400, height: 200))
    let text = selection?.string
}

Mise en surbrillance des résultats de recherche

let results = document.findString("important", withOptions: [.caseInsensitive])
for selection in results { selection.color = .yellow }
pdfView.highlightedSelections = results

if let first = results.first {
    pdfView.setCurrentSelection(first, animate: true)
    pdfView.go(to: first)
}

Annotations

Les annotations sont créées avec PDFAnnotation(bounds:forType:withProperties:) et ajoutées à une PDFPage.

Annotation de surlignement

func addHighlight(to page: PDFPage, selection: PDFSelection) {
    let highlight = PDFAnnotation(
        bounds: selection.bounds(for: page),
        forType: .highlight, withProperties: nil
    )
    highlight.color = UIColor.yellow.withAlphaComponent(0.5)
    page.addAnnotation(highlight)
}

Annotation de note texte

let note = PDFAnnotation(
    bounds: CGRect(x: 100, y: 700, width: 30, height: 30),
    forType: .text, withProperties: nil
)
note.contents = "Ceci est une note adhésive."
note.color = .systemYellow
note.iconType = .comment
page.addAnnotation(note)

Annotation de texte libre

let freeText = PDFAnnotation(
    bounds: CGRect(x: 50, y: 600, width: 300, height: 40),
    forType: .freeText, withProperties: nil
)
freeText.contents = "Commentaire ajouté"
freeText.font = UIFont.systemFont(ofSize: 14)
freeText.fontColor = .darkGray
page.addAnnotation(freeText)

Annotation de lien

let link = PDFAnnotation(
    bounds: CGRect(x: 50, y: 500, width: 200, height: 20),
    forType: .link, withProperties: nil
)
link.url = URL(string: "https://example.com")
page.addAnnotation(link)

// Lien vers une page interne
link.destination = PDFDestination(page: targetPage, at: .zero)

Suppression des annotations

for annotation in page.annotations {
    page.removeAnnotation(annotation)
}

Référence des sous-types d'annotations

Sous-typeConstanteObjectif
Surlignement.highlightBalisage de texte (surlignement jaune)
Soulignement.underlineBalisage de texte (soulignement)
Barré.strikeOutBalisage de texte (barré)
Texte.textIcône de note adhésive
Texte libre.freeTextBloc de texte en ligne
Encre.inkTrajets de dessin à main levée
Lien.linkURL ou destination de page
Ligne.lineLigne droite avec extrémités
Carré.squareForme rectangulaire
Cercle.circleForme elliptique
Tampon.stampTampon en caoutchouc (Approuvé, etc.)
Widget.widgetÉlément de formulaire (champ de texte, case à cocher)

Vignettes

PDFThumbnailView

PDFThumbnailView affiche une bande de vignettes de pages liée à une PDFView.

let thumbnailView = PDFThumbnailView()
thumbnailView.pdfView = pdfView
thumbnailView.thumbnailSize = CGSize(width: 60, height: 80)
thumbnailView.layoutMode = .vertical
thumbnailView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(thumbnailView)

Génération programmatique de vignettes

let thumbnail = page.thumbnail(of: CGSize(width: 120, height: 160), for: .mediaBox)

// Toutes les pages
let thumbnails = (0..<document.pageCount).compactMap {
    document.page(at: $0)?.thumbnail(of: CGSize(width: 120, height: 160), for: .mediaBox)
}

Intégration SwiftUI

Encapsulez PDFView dans un UIViewRepresentable pour SwiftUI.

import SwiftUI
import PDFKit

struct PDFKitView: UIViewRepresentable {
    let document: PDFDocument

    func makeUIView(context: Context) -> PDFView {
        let pdfView = PDFView()
        pdfView.autoScales = true
        pdfView.displayMode = .singlePageContinuous
        pdfView.document = document
        return pdfView
    }

    func updateUIView(_ pdfView: PDFView, context: Context) {
        if pdfView.document !== document {
            pdfView.document = document
        }
    }
}

Utilisation

struct DocumentScreen: View {
    let url: URL

    var body: some View {
        if let document = PDFDocument(url: url) {
            PDFKitView(document: document)
                .ignoresSafeArea()
        } else {
            ContentUnavailableView("Impossible de charger le PDF", systemImage: "doc.questionmark")
        }
    }
}

Pour des wrappers interactifs avec suivi de page, détection de tap sur les annotations et patterns de coordinateur, voir references/pdfkit-patterns.md.

Superpositions de pages (iOS 16+)

PDFPageOverlayViewProvider place des vues UIKit au-dessus de pages individuelles pour des contrôles interactifs ou un rendu personnalisé au-delà des annotations standard.

class OverlayProvider: NSObject, PDFPageOverlayViewProvider {
    func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
        let overlay = UIView()
        // Ajouter des sous-vues personnalisées
        return overlay
    }
}

pdfView.pageOverlayViewProvider = overlayProvider

Erreurs courantes

À NE PAS FAIRE : Forcer le déballage de l'initialiseur PDFDocument

PDFDocument(url:) et PDFDocument(data:) sont des initialiseurs failable.

// FAUX
let document = PDFDocument(url: url)!

// CORRECT
guard let document = PDFDocument(url: url) else { return }

À NE PAS FAIRE : Oublier autoScales sur PDFView

Sans autoScales, le PDF s'affiche à sa résolution native.

// FAUX
pdfView.document = document

// CORRECT
pdfView.autoScales = true
pdfView.document = document

À NE PAS FAIRE : Ignorer le système de coordonnées PDF dans les annotations

Les coordonnées des pages PDF ont l'origine en bas à gauche, avec Y croissant vers le haut — à l'inverse d'UIKit.

// FAUX : Coordonnées UIKit
let bounds = CGRect(x: 50, y: 50, width: 200, height: 30)

// CORRECT : Coordonnées PDF (origine en bas à gauche)
let pageBounds = page.bounds(for: .mediaBox)
let pdfY = pageBounds.height - 50 - 30
let bounds = CGRect(x: 50, y: pdfY, width: 200, height: 30)

À NE PAS FAIRE : Modifier les annotations sur un thread d'arrière-plan

Les classes PDFKit ne sont pas thread-safe.

// FAUX
DispatchQueue.global().async { page.addAnnotation(annotation) }

// CORRECT
DispatchQueue.main.async { page.addAnnotation(annotation) }

À NE PAS FAIRE : Comparer PDFDocument avec == dans UIViewRepresentable

PDFDocument est un type de référence. Utilisez l'identité (!==).

// FAUX : Remplace toujours le document
func updateUIView(_ pdfView: PDFView, context: Context) {
    pdfView.document = document
}

// CORRECT
func updateUIView(_ pdfView: PDFView, context: Context) {
    if pdfView.document !== document {
        pdfView.document = document
    }
}

Checklist de révision

  • L'initialiseur de PDFDocument utilise une liaison optionnelle, pas un force-unwrap
  • pdfView.autoScales = true défini pour un affichage initial correct
  • Les index de pages sont vérifiés par rapport à pageCount avant l'accès
  • displayMode et displayDirection configurés pour correspondre au design
  • Les annotations utilisent l'espace de coordonnées PDF (origine en bas à gauche, Y vers le haut)
  • Toutes les mutations de PDFKit se font sur le thread principal
  • Les PDF protégés par mot de passe sont gérés avec isLocked / unlock(withPassword:)
  • Le wrapper SwiftUI utilise une vérification d'identité !== dans updateUIView
  • La notification PDFViewPageChanged est observée pour le suivi de page
  • PDFThumbnailView.pdfView est lié à la PDFView principale
  • La recherche sur de grands documents utilise beginFindString asynchrone avec délégué
  • Les documents sauvegardés utilisent write(to:withOptions:) en cas de chiffrement nécessaire

Références

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

JordanCoin/ios-skills-collection

Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries. Use when the user asks about GitHub issues, pull requests, workflows, or wants to interact with GitHub repositories from the command line — including tasks like check CI status, create PR, list issues, or query the GitHub API.

JordanCoin/ios-skills-collection

Create media playback experiences using AVKit. Use when adding video players with AVPlayerViewController, enabling Picture-in-Picture, routing media with AirPlay, using SwiftUI VideoPlayer views, configuring transport controls, displaying subtitles and closed captions, or integrating AVFoundation playback with system UI.

JordanCoin/ios-skills-collection

Integrate and optimize Core ML models in iOS apps for on-device machine learning inference. Covers model loading (.mlmodelc, .mlpackage), predictions with auto-generated classes and MLFeatureProvider, compute unit configuration (CPU, GPU, Neural Engine), MLTensor, VNCoreMLRequest, MLComputePlan, multi-model pipelines, and deployment strategies. Use when loading Core ML models, making predictions, configuring compute units, or profiling model performance.

JordanCoin/ios-skills-collection

Implement, review, or improve maps and location features in iOS/macOS apps using MapKit and CoreLocation. Use when working with Map views, annotations, markers, polylines, user location tracking, geocoding, reverse geocoding, search/autocomplete, directions and routes, geofencing, region monitoring, CLLocationUpdate async streams, or location authorization flows. Also use when working with maps, coordinates, addresses, places, directions, distance calculations, or location-based features in Swift apps.

JordanCoin/ios-skills-collection

Implement, review, or improve in-app tips and onboarding using Apple's TipKit framework. Use when adding feature discovery tooltips, onboarding flows, contextual tips, first-run experiences, coach marks, or working with Tip protocol, TipView, popoverTip, tip rules, tip events, or feature education UI.

JordanCoin/ios-skills-collection

When the user wants to optimize their Google Play Store listing — title, short description, full description, keywords, ratings, or Play Store-specific features. Use when the user mentions "Google Play", "Android", "Play Store", "Play Console", "short description", "full description indexed", "Google Play ASO", or wants Google Play-specific keyword, creative, or ratings strategy. For iOS App Store optimization, see aso-audit and metadata-optimization.

JordanCoin/ios-skills-collection

When the user wants to implement, optimize, or use App Clips for app discovery and conversion. Use when the user mentions "App Clip", "app clip code", "mini app", "instant app", "App Clip card", "App Clip link", "no download required", "instant experience", or wants to understand how App Clips appear in App Store search. For general App Store discoverability, see aso-audit. For marketing campaigns, see ua-campaign.

JordanCoin/ios-skills-collection

When the user wants to plan a launch strategy for a new app or major update. Also use when the user mentions "app launch", "launch plan", "launch checklist", "pre-launch", "launch day", or "how to launch my app". For ongoing ASO after launch, see aso-audit. For paid acquisition during launch, see ua-campaign.

JordanCoin/ios-skills-collection

When the user wants to analyze their own app's actual performance data from App Store Connect — real downloads, revenue, IAP, subscriptions, trials, or country breakdowns synced via Appeeky Connect. Use when the user asks about "my downloads", "my revenue", "how is my app performing", "ASC data", "sales and trends", "my subscription numbers", "App Store Connect metrics", or wants to compare periods or top markets. For third-party app estimates, see app-analytics. For subscription analytics depth, see monetization-strategy.

JordanCoin/ios-skills-collection

When the user wants a full ASO health audit, review their App Store listing quality, or diagnose why their app isn't ranking. Also use when the user mentions "ASO audit", "ASO score", "why am I not ranking", "listing review", or "optimize my app store page". For keyword-specific research, see keyword-research. For metadata writing, see metadata-optimization.

JordanCoin/ios-skills-collection

Always use when working on any iOS, macOS, Swift, SwiftUI, Xcode, or Apple platform project. Routes to the correct specialized skills based on development phase and task context. This collection contains 210 skills from 28 authors covering the full iOS lifecycle.

Skills associés