NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

How to let users select pictures using PhotosPicker

Paul Hudson    @twostraws   

Updated for Xcode 14.2

New in iOS 16

SwiftUI’s PhotosPicker brings up the system-standard photo import user interface, allowing users to select one or more images or videos to bring in to your app.

To use it to bring in an image, you need to import the PhotosUI module, then create some storage for a PhotosPickerItem to save what the user selected, and also an Image property to store the loaded asset. You can then watch for the PhotosPickerItem changing, and load its contents like this:

import PhotosUI
import SwiftUI

struct ContentView: View {
    @State private var avatarItem: PhotosPickerItem?
    @State private var avatarImage: Image?

    var body: some View {
        VStack {
            PhotosPicker("Select avatar", selection: $avatarItem, matching: .images)

            if let avatarImage {
                avatarImage
                    .resizable()
                    .scaledToFit()
                    .frame(width: 300, height: 300)

            }
        }
        .onChange(of: avatarItem) { _ in
            Task {
                if let data = try? await avatarItem?.loadTransferable(type: Data.self) {
                    if let uiImage = UIImage(data: data) {
                        avatarImage = Image(uiImage: uiImage)
                        return
                    }
                }

                print("Failed")
            }
        }
    }
}

Download this as an Xcode project

Yes, you need to load a Data object, bounce it through UIImage, then convert that into an Image – the API could be a whole lot simpler, particularly given that Image already conforms to Transerable, but I’m afraid we’re stuck with it.

If you want more control over the data that is selected, adjust the matching parameter based on what you’re looking for:

  • Use matching: .screenshots if you only want screenshots.
  • Use matching: .any(of: [.panoramas, .screenshots]) if you want either of those types.
  • Use matching: .not(.videos) if you want any media that isn’t a video.
  • Use matching: .any(of: [.images, .not(.screenshots)])) if you want all kinds of images except screenshots.

If you want to let the user select multiple images, you should use an array of PhotosPickerItem objects then load them individually using a similar process:

import PhotosUI
import SwiftUI

struct ContentView: View {
    @State private var selectedItems = [PhotosPickerItem]()
    @State private var selectedImages = [Image]()

    var body: some View {
        NavigationStack {
            ScrollView {
                LazyVStack {
                    ForEach(0..<selectedImages.count, id: \.self) { i in
                        selectedImages[i]
                            .resizable()
                            .scaledToFit()
                            .frame(width: 300, height: 300)
                    }
                }
            }
            .toolbar {
                PhotosPicker("Select images", selection: $selectedItems, matching: .images)
            }
            .onChange(of: selectedItems) { _ in
                Task {
                    selectedImages.removeAll()

                    for item in selectedItems {
                        if let data = try? await item.loadTransferable(type: Data.self) {
                            if let uiImage = UIImage(data: data) {
                                let image = Image(uiImage: uiImage)
                                selectedImages.append(image)
                            }
                        }
                    }
                }
            }
        }
    }
}

Download this as an Xcode project

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 1.0/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.