Multiple images selector via PHPicker in iOS iOS 17.10.2023

The PHPickerViewController class is the new system picker that allows you to get access to images and videos from the user’s photo library.

PHPickerViewController is an alternative to UIImagePickerController with ability to select more than one image at once. UIImagePickerController was a good choice before iOS 14.0 as simple image picker.

Contrarily to the UIImagePickerController, PHPicker provides a better privacy and does not require to ask for user consent so it can be used.

UIKit

import UIKit
import PhotosUI

class PhotosUIViewController: UIViewController {
    lazy var addButton: UIButton = {
        let v = UIButton(type: .system)
        v.setTitle("Add", for: .normal)
        v.translatesAutoresizingMaskIntoConstraints = false
        v.addTarget(self, action: #selector(handleAdd), for: .touchUpInside)
        return v
    }()

    lazy var stackView: UIStackView = {
        let v = UIStackView(arrangedSubviews: [addButton])
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.distribution = .fillProportionally
        v.spacing = 8
        v.alignment = .fill
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

extension PhotosUIViewController: PHPickerViewControllerDelegate {
    @objc func handleAdd() {
        var config = PHPickerConfiguration()
        config.selectionLimit = 0
        config.filter = PHPickerFilter.images
        let pickerViewController = PHPickerViewController(configuration: config)
        pickerViewController.delegate = self
        self.present(pickerViewController, animated: true, completion: nil)
    }

    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true, completion: nil)

        for result in results {
            result.itemProvider.loadObject(ofClass: UIImage.self, completionHandler: { [weak self] (object, error) in
                if let image = object as? UIImage {
                    DispatchQueue.main.async { [weak self] in
                        self?.stackView.addArrangedSubview(UIImageView(image: image))
                    }
                }
            })
        }
    }
}

SwiftUI

You can use following snippet starting from iOS 16.0. To use PHPicker on 14.0 iOS version or above you have to wrap it in UIViewControllerRepresentable.

import SwiftUI
import PhotosUI

struct PhotoView: View {
    @State var photoItems = [PhotosPickerItem]()
    @State var selectedPhotos = [UIImage]()

    var body: some View {
        VStack {
            if !selectedPhotos.isEmpty {
                ScrollView(showsIndicators: false) {
                    ForEach(selectedPhotos, id: \.self) { photo in
                        Image(uiImage: photo)
                            .resizable()
                            .scaledToFit()
                            .frame(width: 200, height: 200)
                    }
                }
            }

            PhotosPicker(selection: $photoItems, maxSelectionCount: 0, matching: .images) {
                Label("Select photos", systemImage: "photo")
            }
            .onChange(of: photoItems) { newItems in
                newItems.forEach { item in
                    Task {
                        guard let data = try? await item.loadTransferable(type: Data.self) else { return }
                        guard let image = UIImage(data: data) else { return }
                        selectedPhotos.append(image)
                    }
                }
            }
        }
    }
}