1 // Copyright 2022-present 650 Industries. All rights reserved.
2 
3 import PhotosUI
4 
5 /**
6  Protocol that describes scenarios we care about while the user is picking media.
7  */
8 protocol OnMediaPickingResultHandler {
9   @available(iOS 14, *)
didPickMultipleMedianull10   func didPickMultipleMedia(selection: [PHPickerResult])
11   func didPickMedia(mediaInfo: MediaInfo)
12   func didCancelPicking()
13 }
14 
15 /**
16  This class is responsible for responding to any events that are happening in `UIImagePickerController`.
17  It then forwards them back in unified way via `OnMediaPickingResultHandler`.
18 
19  The functionality of this delegate is separated from the main module class for two reasons:
20  1) main module cannot inherit from `NSObject` (and that's required by three protocols we must conform to),
21  because it already inherits from `Module` class and Swift language does not allow multiple inheritance,
22  2) it separates some logic from the main module class and hopefully makes it cleaner.
23  */
24 internal class ImagePickerHandler: NSObject,
25                                    PHPickerViewControllerDelegate,
26                                    UINavigationControllerDelegate,
27                                    UIImagePickerControllerDelegate,
28                                    UIAdaptivePresentationControllerDelegate {
29   private let onMediaPickingResultHandler: OnMediaPickingResultHandler
30   private let hideStatusBarWhenPresented: Bool
31   private var statusBarVisibilityController = StatusBarVisibilityController()
32 
33   init(onMediaPickingResultHandler: OnMediaPickingResultHandler, hideStatusBarWhenPresented: Bool) {
34     self.onMediaPickingResultHandler = onMediaPickingResultHandler
35     self.hideStatusBarWhenPresented = hideStatusBarWhenPresented
36   }
37 
38   private func handlePickedMedia(mediaInfo: MediaInfo) {
39     statusBarVisibilityController.maybeRestoreStatusBarVisibility()
40     onMediaPickingResultHandler.didPickMedia(mediaInfo: mediaInfo)
41   }
42 
43   @available(iOS 14, *)
44   private func handlePickedMedia(selection: [PHPickerResult]) {
45     statusBarVisibilityController.maybeRestoreStatusBarVisibility()
46     onMediaPickingResultHandler.didPickMultipleMedia(selection: selection)
47   }
48 
49   private func handlePickingCancellation() {
50     statusBarVisibilityController.maybeRestoreStatusBarVisibility()
51     onMediaPickingResultHandler.didCancelPicking()
52   }
53 
54   // MARK: - UIImagePickerControllerDelegate
55 
56   func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: MediaInfo) {
57     DispatchQueue.main.async {
58       picker.dismiss(animated: true) { [weak self] in
59         self?.handlePickedMedia(mediaInfo: info)
60       }
61     }
62   }
63 
64   func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
65     DispatchQueue.main.async {
66       picker.dismiss(animated: true) { [weak self] in
67         self?.handlePickingCancellation()
68       }
69     }
70   }
71 
72   // MARK: - PHPickerViewControllerDelegate
73 
74   @available(iOS 14, *)
75   func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
76     DispatchQueue.main.async {
77       picker.dismiss(animated: true) { [weak self] in
78         // The PHPickerViewController returns empty collection when canceled
79         if results.isEmpty {
80           self?.handlePickingCancellation()
81         } else {
82           self?.handlePickedMedia(selection: results)
83         }
84       }
85     }
86   }
87 
88   // MARK: - UIAdaptivePresentationControllerDelegate
89 
90   func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
91     handlePickingCancellation()
92   }
93 
94   // MARK: - UINavigationControllerDelegate
95 
96   func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
97     statusBarVisibilityController.maybePreserveVisibilityAndHideStatusBar(hideStatusBarWhenPresented)
98   }
99 }
100 
101 /**
102  Protocol that is a common type for supported picker controllers.
103  */
104 internal protocol PickerUIController: UIViewController {
setResultHandlernull105   func setResultHandler(_ handler: ImagePickerHandler)
106 }
107 
108 extension UIImagePickerController: PickerUIController {
109   func setResultHandler(_ handler: ImagePickerHandler) {
110     self.delegate = handler
111     self.presentationController?.delegate = handler
112   }
113 }
114 
115 @available(iOS 14, *)
116 extension PHPickerViewController: PickerUIController {
setResultHandlernull117   func setResultHandler(_ handler: ImagePickerHandler) {
118     self.delegate = handler
119     self.presentationController?.delegate = handler
120   }
121 }
122