WWDC Quick Look 💓 By SwiftGGTeam
What's new in the Photos picker

What's new in the Photos picker

Watch original video

Highlight

iOS 17’s Photos Picker adds inline style, continuous selection, and accessory visibility controls, letting developers seamlessly integrate the system photo picker into their app UI without requesting photo library permission.

Core Content

Building your own photo picker is thankless work. You have to handle album browsing, search, zoomable grids, permission requests, and reassure users you aren’t snooping on their photos.

00:22)The system Photos Picker introduced in iOS 14 solved these problems. It runs in a separate process, and your app only gets photos the user actually selects—no photo library access permission required.

But the previous Picker could only be presented as a modal sheet. iOS 17 lets the Picker embed directly in your app’s SwiftUI view hierarchy.

Embedded Picker

02:47.photosPickerStyle(.inline) embeds the Picker in your app UI. It still runs in a separate process, but visually blends with your app.

The first time you show an embedded picker, the system automatically displays onboarding UI telling users the app can only access selected photos. The picker also shows a privacy badge.

Three Styles

06:11

  • .presentation: Default modal sheet style
  • .inline: Embedded style that fills the specified area
  • .compact: Single-row horizontal scroll, ideal for space-constrained UIs

Continuous Selection

02:40selectionBehavior: .continuous notifies your app in real time as selections change. Each select or deselect immediately updates your app. In embedded mode, you no longer need an “Add” button.

Accessory Controls

02:11.photosPickerAccessoryVisibility controls navigation bar, toolbar, and sidebar visibility. .photosPickerDisabledCapabilities can disable search, album navigation, staging area, and more—replace them with your own UI.

Options Menu

11:21)The new Options menu lets users choose whether to share sensitive metadata (like location). Apps using PhotosPicker or PHPickerViewController get this automatically—no extra work needed.

Detailed Content

Embedded Photos Picker (SwiftUI)

02:47)Embed the Picker in your app UI:

import SwiftUI
import PhotosUI

struct ContentView: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    
    var body: some View {
        VStack {
            // Embedded picker
            PhotosPicker(selection: $selectedItems,
                        selectionBehavior: .continuous) {
                // Do not show a label because the picker renders its own content
            }
            .photosPickerStyle(.inline)
            .photosPickerAccessoryVisibility(.hidden, edges: .all)
            .photosPickerDisabledCapabilities([.search, .selectionActions])
            .frame(height: 300)
            .ignoresSafeArea(.container, edges: .bottom)
            
            // Show selected photos
            SelectedPhotosList(items: selectedItems)
        }
    }
}

Key points:

  • .photosPickerStyle(.inline) switches to embedded mode
  • .photosPickerAccessoryVisibility(.hidden, edges: .all) hides the navigation bar and toolbar
  • .photosPickerDisabledCapabilities([.search, .selectionActions]) disables search and selection buttons
  • selectionBehavior: .continuous enables real-time selection updates
  • .ignoresSafeArea lets the Picker extend to the bottom of the screen

Embedded Picker in UIKit/AppKit

10:02)Use embedded mode in UIKit:

import PhotosUI

class ViewController: UIViewController {
    func setupEmbeddedPicker() {
        var configuration = PHPickerConfiguration(photoLibrary: .shared())
        configuration.selection = .continuous
        configuration.mode = .compact // or .default
        configuration.edgesWithoutContentMargins = [.top, .bottom]
        configuration.disabledCapabilities = [.search]
        
        let picker = PHPickerViewController(configuration: configuration)
        addChild(picker)
        view.addSubview(picker.view)
        
        // Position with Auto Layout
        picker.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            picker.view.topAnchor.constraint(equalTo: view.topAnchor),
            picker.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            picker.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            picker.view.heightAnchor.constraint(equalToConstant: 300)
        ])
        
        picker.didMove(toParent: self)
    }
}

Key points:

  • Set PHPickerConfiguration.selection to .continuous for continuous selection
  • Set mode to .compact for single-row layout
  • edgesWithoutContentMargins hides accessories on specified edges
  • Add as a child view controller

Dynamically Updating Picker Configuration

11:04)Use PHPickerConfiguration.Update to update configuration while the Picker is displayed:

// Update the selection limit
var update = PHPickerConfiguration.Update()
update.selectionLimit = 5
picker.update(with: update)

// Deselect an asset
picker.deselectAsset(withIdentifier: assetIdentifier)

// Reorder selected assets
picker.moveAsset(withIdentifier: identifier, toIndex: newIndex)

Key points:

  • PHPickerConfiguration.Update allows runtime configuration changes
  • deselectAsset and moveAsset manage selected assets

Receiving HDR and Original Format Photos

12:16)Get photos in original format to avoid automatic transcoding:

import UniformTypeIdentifiers

struct PhotoPicker: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    
    var body: some View {
        PhotosPicker(selection: $selectedItems,
                    matching: .images) {
            Text("Select Photos")
        }
        .onChange(of: selectedItems) { items in
            for item in items {
                // Request the original format
                item.loadTransferable(type: .image,
                                     preferredItemEncoding: .current) { result in
                    switch result {
                    case .success(let data):
                        // data contains image data in the original format
                        break
                    case .failure(let error):
                        print(error)
                    }
                }
            }
        }
    }
}

Key points:

  • preferredItemEncoding: .current avoids automatic JPEG transcoding
  • Request .image type to get original format
  • Requesting a specific type like .jpeg may still trigger transcoding

Handling Cinematic Mode Video

13:24)The Picker returns rendered Cinematic videos by default (depth effects baked in). To get editable decision points, you need photo library permission and PhotoKit:

// The picker returns the rendered video
// To get the original Cinematic data, use PhotoKit:

let fetchOptions = PHFetchOptions()
let assets = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: fetchOptions)

if let asset = assets.firstObject {
    // Check whether it is a Cinematic video
    if asset.mediaSubtypes.contains(.videoCinematic) {
        // Request the original video asset through PHImageManager
    }
}

Key points:

  • Cinematic videos from the Picker are rendered versions
  • Editable versions require PhotoKit permission
  • See the “Support Cinematic mode videos in your app” session

Accessory Visibility Control

04:52)Control Picker UI elements:

// Hide all accessories
.photosPickerAccessoryVisibility(.hidden, edges: .all)

// Hide only the bottom toolbar
.photosPickerAccessoryVisibility(.hidden, edges: .bottom)

// Hide the sidebar on iPadOS/macOS
.photosPickerAccessoryVisibility(.hidden, edges: .leading)

Accessory definitions:

  • iOS: top is navigation bar, bottom is toolbar
  • iPadOS/macOS: leading is sidebar, top and bottom same as iOS

Disabling Picker Capabilities

05:12)Disable specific features:

.photosPickerDisabledCapabilities([.search, .collectionNavigation, .stagingArea])

Disableable capabilities:

  • .search: Hide search bar
  • .collectionNavigation: Hide album tabs and sidebar
  • .stagingArea: Replace toolbar buttons with status labels
  • .selectionActions: Hide “Cancel” and “Add” buttons (use with .continuous selection)

Core Takeaways

1. Build a seamless photo selection experience for social apps

What to build: Embed the Photos Picker directly in your post or comment UI, showing selected photos in real time below the input area.

Why it’s worth doing: Traditional sheet mode interrupts the posting flow. An embedded Picker lets users keep editing text while selecting photos for a smoother experience.

How to start: Use .photosPickerStyle(.inline) to embed the Picker, set selectionBehavior: .continuous for real-time updates, and hide the navigation bar and toolbar so the Picker fits your app design.

2. Add a compact selection strip for photo editing apps

What to build: Add a compact horizontal photo strip above the editing toolbar so users can quickly switch between photos to edit.

Why it’s worth doing: The .compact style takes only one row of height—ideal for space-constrained tool UIs. Users can pick new photos without leaving the editing screen.

How to start: Use PHPickerConfiguration with mode: .compact, set a small fixed height, and listen for selection changes to load new images.

3. Build a privacy-first photo management app

What to build: Use the Photos Picker exclusively instead of requesting full photo library permission, letting users precisely control which photos to share.

Why it’s worth doing: The Options menu lets users strip sensitive metadata like location. The Picker’s separate-process design ensures your app only accesses selected photos, building user trust.

How to start: Use the Photos Picker Transferable API to receive images, set preferredItemEncoding: .current to preserve original format and HDR info. Avoid UIImagePickerController or requesting full photo library permission.

4. Add custom album navigation for gallery apps

What to build: Disable the Picker’s built-in album navigation and use your own sidebar or dropdown to switch albums while keeping the Picker’s grid and search.

Why it’s worth doing: Your app may have special album organization (by project, timeline, etc.), and custom navigation fits better into your overall information architecture.

How to start: Use .photosPickerDisabledCapabilities(.collectionNavigation) to hide built-in album navigation, place your custom album list beside the Picker, and pass album content via PhotoKit.

Comments

GitHub Issues · utterances