PhotosPicker is now functional iOs 13-15

This commit is contained in:
Shaps Benkau 2023-02-01 11:30:32 +00:00
parent c4936d438e
commit 1e926bf829
5 changed files with 433 additions and 292 deletions

View File

@ -2,7 +2,7 @@
import SwiftUI
import PhotosUI
@available(iOS, deprecated: 16.0)
@available(iOS, introduced: 14, deprecated: 16.0)
public extension Backport where Wrapped: View {
/// Presents a Photos picker that selects a `PhotosPickerItem` from a given photo library.
///
@ -30,16 +30,13 @@ public extension Backport where Wrapped: View {
}
)
return content.sheet(isPresented: isPresented) {
PhotosPickerView(
selection: binding,
filter: filter,
maxSelection: 1,
selectionBehavior: .default,
encoding: preferredItemEncoding,
library: photoLibrary
)
}
return photosPicker(
isPresented: isPresented,
selection: binding,
matching: filter,
preferredItemEncoding: preferredItemEncoding,
photoLibrary: photoLibrary
)
}
@ -64,16 +61,82 @@ public extension Backport where Wrapped: View {
preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
photoLibrary: PHPhotoLibrary = .shared()
) -> some View {
content.sheet(isPresented: isPresented) {
PhotosPickerView(
selection: selection,
filter: filter,
maxSelection: maxSelectionCount,
selectionBehavior: selectionBehavior,
encoding: preferredItemEncoding,
library: photoLibrary
)
}
content._photoPicker(
isPresented: isPresented,
selection: selection,
filter: filter,
maxSelectionCount: maxSelectionCount,
selectionBehavior: selectionBehavior,
preferredItemEncoding: preferredItemEncoding,
library: photoLibrary
)
}
}
@available(iOS, introduced: 13, deprecated: 14.0)
public extension Backport where Wrapped: View {
/// Presents a Photos picker that selects a `PhotosPickerItem` from a given photo library.
///
/// The user explicitly grants access only to items they choose, so photo library access authorization is not needed.
///
/// - Parameters:
/// - isPresented: The binding to whether the Photos picker should be shown.
/// - selection: The item being shown and selected in the Photos picker.
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of the selected item. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
func photosPicker(
isPresented: Binding<Bool>,
selection: Binding<Backport<Any>.PhotosPickerItem?>,
matching filter: Backport<Any>.PHPickerFilter? = nil,
preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy = .automatic
) -> some View {
let binding = Binding(
get: {
[selection.wrappedValue].compactMap { $0 }
},
set: { newValue in
selection.wrappedValue = newValue.first
}
)
return content._photoPicker(
isPresented: isPresented,
selection: binding,
filter: nil,
maxSelectionCount: 1,
selectionBehavior: .default,
preferredItemEncoding: preferredItemEncoding,
library: .shared()
)
}
/// Presents a Photos picker that selects a collection of `PhotosPickerItem` from a given photo library.
///
/// The user explicitly grants access only to items they choose, so photo library access authorization is not needed.
///
/// - Parameters:
/// - isPresented: The binding to whether the Photos picker should be shown.
/// - selection: All items being shown and selected in the Photos picker.
/// - maxSelectionCount: The maximum number of items that can be selected. Default is `nil`. Setting it to `nil` means maximum supported by the system.
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of selected items. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
func photosPicker(
isPresented: Binding<Bool>,
selection: Binding<[Backport<Any>.PhotosPickerItem]>,
maxSelectionCount: Int? = nil,
matching filter: Backport<Any>.PHPickerFilter? = nil,
preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy = .automatic
) -> some View {
content._photoPicker(
isPresented: isPresented,
selection: selection,
filter: filter,
maxSelectionCount: maxSelectionCount,
selectionBehavior: .default,
preferredItemEncoding: preferredItemEncoding,
library: .shared()
)
}
}

View File

@ -25,19 +25,20 @@ public extension Backport<Any> {
} label: {
label
}
.backport.photosPicker(
._photoPicker(
isPresented: $isPresented,
selection: $selection,
filter: filter,
maxSelectionCount: maxSelection,
selectionBehavior: selectionBehavior,
matching: filter,
preferredItemEncoding: encoding,
photoLibrary: library
library: library
)
}
}
}
@available(iOS, introduced: 13, deprecated: 16)
public extension Backport<Any>.PhotosPicker {
/// Creates a Photos picker that selects a `PhotosPickerItem`.
///
@ -69,7 +70,10 @@ public extension Backport<Any>.PhotosPicker {
self.library = .shared()
self.label = label()
}
}
@available(iOS, introduced: 14, deprecated: 16)
public extension Backport<Any>.PhotosPicker {
/// Creates a Photos picker that selects a `PhotosPickerItem` from a given photo library.
///
/// The user explicitly grants access only to items they choose, so photo library access authorization is not needed.
@ -80,6 +84,7 @@ public extension Backport<Any>.PhotosPicker {
/// - preferredItemEncoding: The encoding disambiguation policy of the selected item. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
/// - photoLibrary: The photo library to choose from.
/// - label: The view that describes the action of choosing an item from the photo library.
@available(iOS 14, *)
init(
selection: Binding<Backport.PhotosPickerItem?>,
matching filter: Backport.PHPickerFilter? = nil,
@ -106,6 +111,7 @@ public extension Backport<Any>.PhotosPicker {
// MARK: Single selection
@available(iOS, introduced: 13, deprecated: 16)
public extension Backport<Any>.PhotosPicker<Text> {
/// Creates a Photos picker with its label generated from a localized string key that selects a `PhotosPickerItem`.
///
@ -179,6 +185,7 @@ public extension Backport<Any>.PhotosPicker<Text> {
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of the selected item. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
/// - photoLibrary: The photo library to choose from.
@available(iOS 14, *)
init(
_ titleKey: LocalizedStringKey,
selection: Binding<Backport.PhotosPickerItem?>,
@ -212,6 +219,7 @@ public extension Backport<Any>.PhotosPicker<Text> {
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of the selected item. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
/// - photoLibrary: The photo library to choose from.
@available(iOS 14, *)
init<S>(
_ title: S,
selection: Binding<Backport.PhotosPickerItem?>,
@ -236,8 +244,9 @@ public extension Backport<Any>.PhotosPicker<Text> {
}
}
// MARK: Multiple selection
// MARK: Multiple selection (iOS 15+)
@available(iOS 15, *)
public extension Backport<Any>.PhotosPicker {
/// Creates a Photos picker that selects a collection of `PhotosPickerItem`.
///
@ -298,6 +307,7 @@ public extension Backport<Any>.PhotosPicker {
}
}
@available(iOS 15, *)
public extension Backport<Any>.PhotosPicker<Text> {
/// Creates a Photos picker with its label generated from a localized string key that selects a collection of `PhotosPickerItem`.
///
@ -415,4 +425,90 @@ public extension Backport<Any>.PhotosPicker<Text> {
self.label = Text(title)
}
}
// MARK: Multiple selection (iOS 13+)
@available(iOS 13, *)
public extension Backport<Any>.PhotosPicker {
/// Creates a Photos picker that selects a collection of `PhotosPickerItem`.
///
/// The user explicitly grants access only to items they choose, so photo library access authorization is not needed.
///
/// - Parameters:
/// - selection: All items being shown and selected in the Photos picker.
/// - maxSelectionCount: The maximum number of items that can be selected. Default is `nil`. Setting it to `nil` means maximum supported by the system.
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of selected items. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
/// - label: The view that describes the action of choosing items from the photo library.
init(
selection: Binding<[Backport.PhotosPickerItem]>,
maxSelectionCount: Int? = nil,
matching filter: Backport.PHPickerFilter? = nil,
preferredItemEncoding: Backport.PhotosPickerItem.EncodingDisambiguationPolicy = .automatic,
@ViewBuilder label: () -> Label
) {
_selection = selection
self.filter = filter
self.maxSelection = maxSelectionCount
self.selectionBehavior = .default
self.encoding = preferredItemEncoding
self.library = .shared()
self.label = label()
}
}
@available(iOS 13, *)
public extension Backport<Any>.PhotosPicker<Text> {
/// Creates a Photos picker with its label generated from a localized string key that selects a collection of `PhotosPickerItem`.
///
/// The user explicitly grants access only to items they choose, so photo library access authorization is not needed.
///
/// - Parameters:
/// - titleKey: A localized string key that describes the purpose of showing the picker.
/// - selection: All items being shown and selected in the Photos picker.
/// - maxSelectionCount: The maximum number of items that can be selected. Default is `nil`. Setting it to `nil` means maximum supported by the system.
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of selected items. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
init(
_ titleKey: LocalizedStringKey,
selection: Binding<[Backport.PhotosPickerItem]>,
maxSelectionCount: Int? = nil,
matching filter: Backport.PHPickerFilter? = nil,
preferredItemEncoding: Backport.PhotosPickerItem.EncodingDisambiguationPolicy = .automatic
) {
_selection = selection
self.filter = filter
self.maxSelection = maxSelectionCount
self.selectionBehavior = .default
self.encoding = preferredItemEncoding
self.library = .shared()
self.label = Text(titleKey)
}
/// Creates a Photos picker with its label generated from a string that selects a collection of `PhotosPickerItem`.
///
/// The user explicitly grants access only to items they choose, so photo library access authorization is not needed.
///
/// - Parameters:
/// - title: A string that describes the purpose of showing the picker.
/// - selection: All items being shown and selected in the Photos picker.
/// - maxSelectionCount: The maximum number of items that can be selected. Default is `nil`. Setting it to `nil` means maximum supported by the system.
/// - filter: Types of items that can be shown. Default is `nil`. Setting it to `nil` means all supported types can be shown.
/// - preferredItemEncoding: The encoding disambiguation policy of selected items. Default is `.automatic`. Setting it to `.automatic` means the best encoding determined by the system will be used.
init<S>(
_ title: S,
selection: Binding<[Backport.PhotosPickerItem]>,
maxSelectionCount: Int? = nil,
matching filter: Backport.PHPickerFilter? = nil,
preferredItemEncoding: Backport.PhotosPickerItem.EncodingDisambiguationPolicy = .automatic
) where S: StringProtocol {
_selection = selection
self.filter = filter
self.maxSelection = maxSelectionCount
self.selectionBehavior = .default
self.encoding = preferredItemEncoding
self.library = .shared()
self.label = Text(title)
}
}
#endif

View File

@ -3,7 +3,7 @@ import SwiftUI
import PhotosUI
@available(iOS, deprecated: 16)
public extension Backport where Wrapped == Any {
public extension Backport<Any> {
/// An item can contain multiple representations. Each representation has a corresponding content type.
struct PhotosPickerItem: Equatable, Hashable {
/// A policy that decides the encoding to use given a content type, if multiple encodings are available.
@ -19,10 +19,9 @@ public extension Backport where Wrapped == Any {
/// Uses the most compatible encoding if possible, even if transcoding is required.
public static let compatible: Self = .init(rawValue: 2)
@available(iOS, introduced: 14)
public var mode: PHPickerConfiguration.AssetRepresentationMode {
@available(iOS 14, *)
internal var mode: PHPickerConfiguration.AssetRepresentationMode {
switch self {
case .automatic: return .automatic
case .current: return .current
case .compatible: return .compatible
default: return .automatic

View File

@ -6,11 +6,25 @@ import CoreServices
@available(iOS, introduced: 13, deprecated: 16)
public extension Backport where Wrapped == Any {
/// A filter that restricts which types of assets to show
struct PHPickerFilter: Equatable, Hashable {
internal let predicate: NSPredicate
struct PHPickerFilter {
internal let mediaTypes: [CFString]
internal let filter: Any?
internal init(predicate: NSPredicate) {
self.predicate = predicate
@available(iOS 13, *)
internal init(mediaTypes: [CFString]) {
self.mediaTypes = mediaTypes
self.filter = nil
}
@available(iOS 14, *)
internal init(filter: PhotosUI.PHPickerFilter) {
self.filter = filter
self.mediaTypes = []
}
@available(iOS 14, *)
var photosFilter: PhotosUI.PHPickerFilter {
filter as! PhotosUI.PHPickerFilter
}
}
}
@ -19,71 +33,90 @@ public extension Backport where Wrapped == Any {
public extension Backport<Any>.PHPickerFilter {
/// The filter for images.
static var images: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaType.image]))
if #available(iOS 14, *) {
return .init(filter: .images)
} else {
return .init(mediaTypes: [kUTTypeImage])
}
}
/// The filter for videos.
static var videos: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaType.video]))
if #available(iOS 14, *) {
return .init(filter: .videos)
} else {
return .init(mediaTypes: [kUTTypeMovie])
}
}
/// The filter for live photos.
static var livePhotos: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.photoLive]))
if #available(iOS 14, *) {
return .init(filter: .livePhotos)
} else {
return .init(mediaTypes: [kUTTypeLivePhoto, kUTTypeMovie])
}
}
}
/// The filter for Depth Effect photos.
static var depthEffectPhotos: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.photoDepthEffect]))
@available(iOS 14, *)
public extension Backport<Any>.PHPickerFilter {
/// Returns a new filter formed by AND-ing the filters in a given array.
static func any(of subfilters: [Self]) -> Self {
.init(filter: .any(of: subfilters.map { $0.photosFilter }))
}
}
@available(iOS 15, *)
public extension Backport<Any>.PHPickerFilter {
/// The filter for panorama photos.
static var panoramas: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.photoPanorama]))
.init(filter: .panoramas)
}
/// The filter for screenshots.
static var screenshots: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.photoScreenshot]))
.init(filter: .screenshots)
}
/// The filter for Slow-Mo videos.
static var slomoVideos: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.videoHighFrameRate]))
.init(filter: .slomoVideos)
}
/// The filter for time-lapse videos.
static var timelapseVideos: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.videoTimelapse]))
.init(filter: .timelapseVideos)
}
/// Returns a new filter based on the asset playback style.
static func playbackStyle(_ playbackStyle: PHAsset.PlaybackStyle) -> Self {
.init(predicate: NSPredicate(format: "(playbackStyle & %d) != 0", argumentArray: [playbackStyle.rawValue]))
}
/// Returns a new filter formed by OR-ing the filters in a given array.
static func any(of subfilters: [Self]) -> Self {
.init(predicate: NSCompoundPredicate(orPredicateWithSubpredicates: subfilters.map { $0.predicate }))
.init(filter: .playbackStyle(playbackStyle))
}
/// Returns a new filter formed by AND-ing the filters in a given array.
static func all(of subfilters: [Self]) -> Self {
.init(predicate: NSCompoundPredicate(andPredicateWithSubpredicates: subfilters.map { $0.predicate }))
.init(filter: .all(of: subfilters.map { $0.photosFilter }))
}
/// Returns a new filter formed by negating the given filter.
static func not(_ filter: Self) -> Self {
.init(predicate: NSCompoundPredicate(notPredicateWithSubpredicate: filter.predicate))
.init(filter: .not(filter.photosFilter))
}
}
@available(iOS 15.0, *)
@available(iOS 16, *)
public extension Backport<Any>.PHPickerFilter {
/// The filter for Depth Effect photos.
static var depthEffectPhotos: Self {
.init(filter: .depthEffectPhotos)
}
/// The filter for Cinematic videos.
static var cinematicVideos: Self {
.init(predicate: NSPredicate(format: "(mediaSubtypes & %d) != 0", argumentArray: [PHAssetMediaSubtype.videoCinematic]))
.init(filter: .cinematicVideos)
}
}
#endif

View File

@ -1,239 +1,189 @@
//#if os(iOS)
//import SwiftUI
//import PhotosUI
//
//internal extension View {
// @ViewBuilder
// func _photoPicker(
// isPresented: Binding<Bool>,
// selection: Binding<[Backport<Any>.PhotosPickerItem]>,
// filter: Backport<Any>.PHPickerFilter?,
// maxSelectionCount: Int?,
// selectionBehavior: Backport<Any>.PhotosPickerSelectionBehavior,
// preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy,
// library: PHPhotoLibrary
// ) -> some View {
// if #available(iOS 14, *) {
// backport.background {
// PhotosViewController(
// isPresented: isPresented,
// selection: selection,
// filter: filter,
// maxSelectionCount: maxSelectionCount,
// selectionBehavior: selectionBehavior,
// preferredItemEncoding: preferredItemEncoding,
// library: library
// )
// }
// } else {
// backport.background {
// LegacyPhotosViewController(isPresented: isPresented, selection: selection, filter: filter)
// }
// }
// }
//}
//
//@available(iOS 14, *)
//private struct PhotosViewController: UIViewControllerRepresentable {
// @Binding var isPresented: Bool
// @Binding var selection: [Backport<Any>.PhotosPickerItem]
//
// let options: PHFetchOptions
//
// init(isPresented: Binding<Bool>, selection: Binding<[Backport<Any>.PhotosPickerItem]>, filter: Backport<Any>.PHPickerFilter?, maxSelectionCount: Int?, selectionBehavior: Backport<Any>.PhotosPickerSelectionBehavior, preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy, library: PHPhotoLibrary) {
// _isPresented = isPresented
// _selection = selection
//
// options = PHFetchOptions()
// options.predicate = filter?.predicate
//
// if #available(iOS 15, *) {
// configuration.selection = selectionBehavior.behaviour
// }
//
// self.configuration = configuration
// }
//
// func makeUIViewController(context: Context) -> Representable {
// Representable(
// isPresented: $isPresented,
// selection: $selection,
// configuration: configuration
// )
// }
//
//
// func updateUIViewController(_ controller: Representable, context: Context) {
// controller.selection = $selection
// controller.configuration = configuration
// }
//}
//
//@available(iOS 14, *)
//private extension PhotosViewController {
// final class Representable: UIViewController, PHPickerViewControllerDelegate, UIAdaptivePresentationControllerDelegate {
// private weak var controller: PHPickerViewController?
//
// var isPresented: Binding<Bool> {
// didSet {
// updateControllerLifecycle(
// from: oldValue.wrappedValue,
// to: isPresented.wrappedValue
// )
// }
// }
//
// var selection: Binding<[Backport<Any>.PhotosPickerItem]>
// var configuration: PHPickerConfiguration
//
// init(isPresented: Binding<Bool>, selection: Binding<[Backport<Any>.PhotosPickerItem]>, configuration: PHPickerConfiguration) {
// self.isPresented = isPresented
// self.selection = selection
// self.configuration = configuration
//
// super.init(nibName: nil, bundle: nil)
// }
//
// required init?(coder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
// }
//
// private func updateControllerLifecycle(from oldValue: Bool, to newValue: Bool) {
// switch (oldValue, newValue) {
// case (false, true):
// presentController()
// case (true, false):
// dismissController()
// case (true, true), (false, false):
// break
// }
// }
//
// private func presentController() {
// let controller = PHPickerViewController(configuration: configuration)
// controller.presentationController?.delegate = self
// controller.delegate = self
// present(controller, animated: true)
// self.controller = controller
// }
//
// private func dismissController() {
// isPresented.wrappedValue = false
// guard let controller else { return }
// controller.presentedViewController?.dismiss(animated: true)
// }
//
// func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// print(results)
// dismissController()
// }
//
// func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
// dismissController()
// }
// }
//}
//
//@available(iOS 13, *)
//private struct LegacyPhotosViewController: UIViewControllerRepresentable {
// @Binding var isPresented: Bool
// @Binding var selection: [Backport<Any>.PhotosPickerItem]
//
// let filter: Backport<Any>.PHPickerFilter?
//
// func makeUIViewController(context: Context) -> Representable {
// Representable(
// isPresented: $isPresented,
// selection: $selection,
// filter: filter
// )
// }
//
//
// func updateUIViewController(_ controller: Representable, context: Context) {
// controller.selection = $selection
// controller.filter = filter
// }
//}
//
//@available(iOS 13, *)
//private extension LegacyPhotosViewController {
// final class Representable: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIAdaptivePresentationControllerDelegate {
// private weak var controller: UIImagePickerController?
//
// var isPresented: Binding<Bool> {
// didSet {
// updateControllerLifecycle(
// from: oldValue.wrappedValue,
// to: isPresented.wrappedValue
// )
// }
// }
//
// var selection: Binding<[Backport<Any>.PhotosPickerItem]>
// var filter: Backport<Any>.PHPickerFilter?
//
// init(isPresented: Binding<Bool>, selection: Binding<[Backport<Any>.PhotosPickerItem]>, filter: Backport<Any>.PHPickerFilter?) {
// self.isPresented = isPresented
// self.selection = selection
// self.filter = filter
//
// super.init(nibName: nil, bundle: nil)
// }
//
// required init?(coder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
// }
//
// private func updateControllerLifecycle(from oldValue: Bool, to newValue: Bool) {
// switch (oldValue, newValue) {
// case (false, true):
// presentController()
// case (true, false):
// dismissController()
// case (true, true), (false, false):
// break
// }
// }
//
// private func presentController() {
// let controller = UIImagePickerController()
//
// if let filter {
// controller.mediaTypes = filter.mediaTypes
// } else if let types = UIImagePickerController.availableMediaTypes(for: .photoLibrary) {
// controller.mediaTypes = types
// }
//
// controller.allowsEditing = false
// controller.sourceType = .photoLibrary
// controller.videoQuality = .typeHigh
// controller.presentationController?.delegate = self
// controller.delegate = self
//
// present(controller, animated: true)
// self.controller = controller
// }
//
// private func dismissController() {
// isPresented.wrappedValue = false
// guard let controller else { return }
// controller.presentedViewController?.dismiss(animated: true)
// }
//
// func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
// print("TBD")
// print(info)
// dismissController()
// }
//
// func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// dismissController()
// }
//
// func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
// dismissController()
// }
// }
//}
//#endif
#if os(iOS)
import SwiftUI
import PhotosUI
internal extension View {
@ViewBuilder
func _photoPicker(
isPresented: Binding<Bool>,
selection: Binding<[Backport<Any>.PhotosPickerItem]>,
filter: Backport<Any>.PHPickerFilter?,
maxSelectionCount: Int?,
selectionBehavior: Backport<Any>.PhotosPickerSelectionBehavior,
preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy,
library: PHPhotoLibrary
) -> some View {
if #available(iOS 14, *) {
sheet(isPresented: isPresented) {
PhotosViewController(
isPresented: isPresented,
selection: selection,
filter: filter,
maxSelectionCount: maxSelectionCount,
selectionBehavior: selectionBehavior,
preferredItemEncoding: preferredItemEncoding,
library: library
)
.ignoresSafeArea()
}
} else {
sheet(isPresented: isPresented) {
LegacyPhotosViewController(
isPresented: isPresented,
selection: selection,
filter: filter,
preferredItemEncoding: preferredItemEncoding
)
.edgesIgnoringSafeArea(.all)
}
}
}
}
@available(iOS 14, *)
private struct PhotosViewController: UIViewControllerRepresentable {
@Binding var isPresented: Bool
@Binding var selection: [Backport<Any>.PhotosPickerItem]
let configuration: PHPickerConfiguration
init(isPresented: Binding<Bool>, selection: Binding<[Backport<Any>.PhotosPickerItem]>, filter: Backport<Any>.PHPickerFilter?, maxSelectionCount: Int?, selectionBehavior: Backport<Any>.PhotosPickerSelectionBehavior, preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy, library: PHPhotoLibrary) {
_isPresented = isPresented
_selection = selection
var configuration = PHPickerConfiguration(photoLibrary: library)
configuration.preferredAssetRepresentationMode = preferredItemEncoding.mode
configuration.selectionLimit = maxSelectionCount ?? 0
configuration.filter = filter?.photosFilter
if #available(iOS 15, *) {
configuration.selection = selectionBehavior.behaviour
}
self.configuration = configuration
}
func makeCoordinator() -> Coordinator {
Coordinator(isPresented: $isPresented, selection: $selection, configuration: configuration)
}
func makeUIViewController(context: Context) -> UIViewController {
context.coordinator.controller
}
func updateUIViewController(_ controller: UIViewController, context: Context) {
context.coordinator.isPresented = $isPresented
context.coordinator.selection = $selection
context.coordinator.configuration = configuration
}
}
@available(iOS 14, *)
private extension PhotosViewController {
final class Coordinator: NSObject, PHPickerViewControllerDelegate, UIAdaptivePresentationControllerDelegate {
var isPresented: Binding<Bool>
var selection: Binding<[Backport<Any>.PhotosPickerItem]>
var configuration: PHPickerConfiguration
lazy var controller: PHPickerViewController = {
let controller = PHPickerViewController(configuration: configuration)
controller.presentationController?.delegate = self
controller.delegate = self
return controller
}()
init(isPresented: Binding<Bool>, selection: Binding<[Backport<Any>.PhotosPickerItem]>, configuration: PHPickerConfiguration) {
self.isPresented = isPresented
self.selection = selection
self.configuration = configuration
super.init()
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
isPresented.wrappedValue = false
}
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
isPresented.wrappedValue = false
}
}
}
@available(iOS 13, *)
private struct LegacyPhotosViewController: UIViewControllerRepresentable {
@Binding var isPresented: Bool
@Binding var selection: [Backport<Any>.PhotosPickerItem]
let filter: Backport<Any>.PHPickerFilter?
var preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy
func makeCoordinator() -> Coordinator {
Coordinator(isPresented: $isPresented, selection: $selection, filter: filter, preferredItemEncoding: preferredItemEncoding)
}
func makeUIViewController(context: Context) -> UIViewController {
context.coordinator.controller
}
func updateUIViewController(_ controller: UIViewController, context: Context) {
context.coordinator.isPresented = $isPresented
context.coordinator.selection = $selection
context.coordinator.filter = filter
context.coordinator.preferredItemEncoding = preferredItemEncoding
}
}
private extension LegacyPhotosViewController {
final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIAdaptivePresentationControllerDelegate {
var isPresented: Binding<Bool>
var selection: Binding<[Backport<Any>.PhotosPickerItem]>
var filter: Backport<Any>.PHPickerFilter?
var preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy
lazy var controller: UIImagePickerController = {
let controller = UIImagePickerController()
if let filter {
controller.mediaTypes = filter.mediaTypes.map { String($0) }
} else if let types = UIImagePickerController.availableMediaTypes(for: .photoLibrary) {
controller.mediaTypes = types
}
controller.allowsEditing = false
controller.sourceType = .photoLibrary
controller.videoQuality = .typeHigh
controller.presentationController?.delegate = self
controller.delegate = self
switch preferredItemEncoding {
case .compatible:
controller.imageExportPreset = .compatible
controller.videoExportPreset = AVAssetExportPresetHighestQuality
case .current:
controller.imageExportPreset = .current
controller.videoExportPreset = AVAssetExportPresetPassthrough
default:
controller.imageExportPreset = .compatible
controller.videoExportPreset = AVAssetExportPresetHEVCHighestQuality
}
return controller
}()
init(isPresented: Binding<Bool>, selection: Binding<[Backport<Any>.PhotosPickerItem]>, filter: Backport<Any>.PHPickerFilter?, preferredItemEncoding: Backport<Any>.PhotosPickerItem.EncodingDisambiguationPolicy) {
self.isPresented = isPresented
self.selection = selection
self.filter = filter
self.preferredItemEncoding = preferredItemEncoding
super.init()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
isPresented.wrappedValue = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isPresented.wrappedValue = false
}
}
}
#endif