You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
15 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. <img src="./Images/tlphotologo.png">
  2. [![Version](https://img.shields.io/cocoapods/v/TLPhotoPicker.svg?style=flat)](http://cocoapods.org/pods/TLPhotoPicker)
  3. [![License](https://img.shields.io/cocoapods/l/TLPhotoPicker.svg?style=flat)](http://cocoapods.org/pods/TLPhotoPicker)
  4. [![Platform](https://img.shields.io/cocoapods/p/TLPhotoPicker.svg?style=flat)](http://cocoapods.org/pods/TLPhotoPicker)
  5. ![Swift](https://img.shields.io/badge/%20in-swift%205.0-orange.svg)
  6. ## Written in Swift 5.0
  7. TLPhotoPicker enables application to pick images and videos from multiple smart album in iOS, similar to the current facebook app.
  8. ## Demo 🙉
  9. | Facebook Picker | TLPhotoPicker |
  10. | ------------- | ------------- |
  11. | ![Facebook Picker](Images/facebook_ex.gif) | ![TLPhotoPicker](Images/tlphotopicker_ex.gif) |
  12. ## Features
  13. - support smart album collection.
  14. - camera roll, selfies, panoramas, favorites, videos, custom users album
  15. - selected order index.
  16. - playback video and live photos.
  17. - just one. playback first video or live Photo in bounds of visible cell.
  18. - display video duration.
  19. - async phasset request and displayed cell.
  20. - scrolling performance is better than facebook in displaying video assets collection.
  21. - custom cell
  22. - custom display and selection rules
  23. - reload of changes that occur in the Photos library.
  24. - support iCloud Photo Library
  25. - adds long press preview to images. ( to @smeshko ) [Preview](https://github.com/tilltue/TLPhotoPicker/pull/252#issue-362005178)
  26. | Smart album collection | LivePhotoCell | VideoPhotoCell | PhotoCell | CustomCell(instagram) |
  27. | ------------- | ------------- | ------------- | ------------- | ------------- |
  28. | ![Facebook Picker](Images/smartalbum.png) | ![LivePhotoCell](Images/livephotocell.png) | ![VideoPhotoCell](Images/videophotocell.png) | ![PhotoCell](Images/photocell.png) | ![PhotoCell](Images/customcell.png) |
  29. Custom Camera Cell
  30. | Live CameraCell |
  31. | ------------- |
  32. | ![Like Line](Images/custom_cameracell.gif)
  33. ## Installation
  34. ### Requirements
  35. - Swift 5.0 ( Swift 4.2 -> use 'version 1.8.3' )
  36. - iOS 9.1 (for use live photos)
  37. ### Cocoapods
  38. TLPhotoPicker is available through [CocoaPods](http://cocoapods.org). To install
  39. it, simply add the following line to your Podfile:
  40. ```ruby
  41. platform :ios, '9.1'
  42. pod "TLPhotoPicker"
  43. ```
  44. ### Carthage
  45. Carthage is a simple, decentralized dependency manager for Cocoa.
  46. Specify TLPhotoPicker into your project's Cartfile:
  47. ```
  48. github "tilltue/TLPhotoPicker"
  49. ```
  50. > Don't forget the Privacy Description in `info.plist`.
  51. <img src="./Images/Privacy.png">
  52. ## Usage
  53. **use delegate**
  54. You can choose delegate method or closure for handle picker event.
  55. ```swift
  56. class ViewController: UIViewController,TLPhotosPickerViewControllerDelegate {
  57. var selectedAssets = [TLPHAsset]()
  58. @IBAction func pickerButtonTap() {
  59. let viewController = TLPhotosPickerViewController()
  60. viewController.delegate = self
  61. var configure = TLPhotosPickerConfigure()
  62. //configure.nibSet = (nibName: "CustomCell_Instagram", bundle: Bundle.main) // If you want use your custom cell..
  63. self.present(viewController, animated: true, completion: nil)
  64. }
  65. //TLPhotosPickerViewControllerDelegate
  66. func dismissPhotoPicker(withTLPHAssets: [TLPHAsset]) {
  67. // use selected order, fullresolution image
  68. self.selectedAssets = withTLPHAssets
  69. }
  70. func dismissPhotoPicker(withPHAssets: [PHAsset]) {
  71. // if you want to used phasset.
  72. }
  73. func photoPickerDidCancel() {
  74. // cancel
  75. }
  76. func dismissComplete() {
  77. // picker viewcontroller dismiss completion
  78. }
  79. func canSelectAsset(phAsset: PHAsset) -> Bool {
  80. //Custom Rules & Display
  81. //You can decide in which case the selection of the cell could be forbidden.
  82. }
  83. func didExceedMaximumNumberOfSelection(picker: TLPhotosPickerViewController) {
  84. // exceed max selection
  85. }
  86. func handleNoAlbumPermissions(picker: TLPhotosPickerViewController) {
  87. // handle denied albums permissions case
  88. }
  89. func handleNoCameraPermissions(picker: TLPhotosPickerViewController) {
  90. // handle denied camera permissions case
  91. }
  92. }
  93. ```
  94. **use closure**
  95. ```swift
  96. init(withPHAssets: (([PHAsset]) -> Void)? = nil, didCancel: ((Void) -> Void)? = nil)
  97. init(withTLPHAssets: (([TLPHAsset]) -> Void)? = nil, didCancel: ((Void) -> Void)? = nil)
  98. var canSelectAsset: ((PHAsset) -> Bool)? = nil
  99. var didExceedMaximumNumberOfSelection: ((TLPhotosPickerViewController) -> Void)? = nil
  100. var handleNoAlbumPermissions: ((TLPhotosPickerViewController) -> Void)? = nil
  101. var handleNoCameraPermissions: ((TLPhotosPickerViewController) -> Void)? = nil
  102. var dismissCompletion: (() -> Void)? = nil
  103. ```
  104. ```swift
  105. class ViewController: UIViewController,TLPhotosPickerViewControllerDelegate {
  106. var selectedAssets = [TLPHAsset]()
  107. @IBAction func pickerButtonTap() {
  108. let viewController = TLPhotosPickerViewController(withTLPHAssets: { [weak self] (assets) in // TLAssets
  109. self?.selectedAssets = assets
  110. }, didCancel: nil)
  111. viewController.didExceedMaximumNumberOfSelection = { [weak self] (picker) in
  112. //exceed max selection
  113. }
  114. viewController.handleNoAlbumPermissions = { [weak self] (picker) in
  115. // handle denied albums permissions case
  116. }
  117. viewController.handleNoCameraPermissions = { [weak self] (picker) in
  118. // handle denied camera permissions case
  119. }
  120. viewController.selectedAssets = self.selectedAssets
  121. self.present(viewController, animated: true, completion: nil)
  122. }
  123. }
  124. ```
  125. **Custom Cell**
  126. Custom Cell must subclass TLPhotoCollectionViewCell
  127. ```Swift
  128. class CustomCell_Instagram: TLPhotoCollectionViewCell {
  129. }
  130. //If you want custom camera cell?
  131. //only used camera cell
  132. [Sample](https://github.com/tilltue/TLPhotoPicker/blob/master/Example/TLPhotoPicker/CustomCameraCell.swift)
  133. //Adding the possibility to handle cell display according to a specific conditions
  134. func update(with phAsset: PHAsset)
  135. func selectedCell()
  136. func willDisplayCell()
  137. func endDisplayingCell()
  138. ```
  139. **Custom Rules & Display**
  140. You can implement your own rules to handle the cell display. You can decide in which case the selection of the cell could be forbidden.
  141. For example, if you want to disable the selection of a cell if its width is under 300, you can follow these steps:
  142. - Override the update method of your custom cell and add your own display rule
  143. ```swift
  144. override func update(with phAsset: PHAsset) {
  145. super.update(with: phAsset)
  146. self.sizeRequiredOverlayView?.isHidden = !(phAsset.pixelHeight <= 300 && phAsset.pixelWidth <= 300)
  147. }
  148. ```
  149. In this code, we show an overlay when the height and width required values are not satisified.
  150. - When you instanciate a `TLPhotosPickerViewController` subclass, you can pass a closure called `canSelectAsset` to handle the selection according to some rules. ( or delegate)
  151. ```Swift
  152. //use delegate
  153. public protocol TLPhotosPickerViewControllerDelegate: class {
  154. ...
  155. func canSelectAsset(phAsset: PHAsset) -> Bool
  156. ...
  157. }
  158. extension UserViewController: TLPhotosPickerViewControllerDelegate {
  159. func canSelectAsset(phAsset: PHAsset) -> Bool {
  160. if asset.pixelHeight < 100 || asset.pixelWidth < 100 {
  161. self?.showUnsatisifiedSizeAlert(vc: viewController)
  162. return false
  163. }
  164. return true
  165. }
  166. }
  167. //or use closure
  168. viewController.canSelectAsset = { [weak self] asset -> Bool in
  169. if asset.pixelHeight < 100 || asset.pixelWidth < 100 {
  170. self?.showUnsatisifiedSizeAlert(vc: viewController)
  171. return false
  172. }
  173. return true
  174. }
  175. ```
  176. In this code, we show an alert when the condition in the closure are not satisfiied.
  177. **TLPHAsset**
  178. ```swift
  179. public struct TLPHAsset {
  180. public enum AssetType {
  181. case photo,video,livePhoto
  182. }
  183. // phasset
  184. public var phAsset: PHAsset? = nil
  185. // selected order index
  186. public var selectedOrder: Int = 0
  187. // asset type
  188. public var type: AssetType
  189. // get full resolution image
  190. public var fullResolutionImage: UIImage?
  191. // get photo file size (async)
  192. public func photoSize(options: PHImageRequestOptions? = nil ,completion: @escaping ((Int)->Void), livePhotoVideoSize: Bool = false)
  193. // get video file size (async)
  194. public func videoSize(options: PHVideoRequestOptions? = nil, completion: @escaping ((Int)->Void))
  195. // get async icloud image (download)
  196. @discardableResult
  197. public func cloudImageDownload(progressBlock: @escaping (Double) -> Void, completionBlock:@escaping (UIImage?)-> Void ) -> PHImageRequestID?
  198. // get original media file async copy temporary media file ( photo(png,gif...etc.) and video ) -> Don't forget, You should delete temporary file.
  199. // parmeter : convertLivePhotosToJPG
  200. // false : If you want mov file at live photos
  201. // true : If you want png file at live photos ( HEIC )
  202. public func tempCopyMediaFile(videoRequestOptions: PHVideoRequestOptions? = nil,
  203. imageRequestOptions: PHImageRequestOptions? = nil,
  204. livePhotoRequestOptions: PHLivePhotoRequestOptions? = nil,
  205. exportPreset: String = AVAssetExportPresetHighestQuality,
  206. convertLivePhotosToJPG: Bool = false,
  207. progressBlock:((Double) -> Void)? = nil,
  208. completionBlock:@escaping ((URL,String) -> Void)) -> PHImageRequestID?
  209. //Apparently, This is not the only way to export video.
  210. //There is many way that export a video.
  211. //This method was one of them.
  212. public func exportVideoFile(options: PHVideoRequestOptions? = nil,
  213. outputURL: URL? = nil,
  214. outputFileType: AVFileType = .mov,
  215. progressBlock:((Double) -> Void)? = nil,
  216. completionBlock:@escaping ((URL,String) -> Void))
  217. // get original asset file name
  218. public var originalFileName: String?
  219. }
  220. ```
  221. > Note: convenience export method
  222. > fullResolutionImage, cloudImageDownload, tempCopyMediaFile, exportVideoFile
  223. > It's not enough if you wanted to use more complicated export asset options. ( progress, export type, etc..)
  224. ## Customize
  225. ```swift
  226. let viewController = TLPhotosPickerViewController()
  227. var configure = TLPhotosPickerConfigure()
  228. viewController.configure = configure
  229. public struct TLPhotosPickerConfigure {
  230. public var customLocalizedTitle: [String: String] = ["Camera Roll": "Camera Roll"] // Set [:] if you want use default localized title of album
  231. public var tapHereToChange = "Tap here to change"
  232. public var cancelTitle = "Cancel"
  233. public var doneTitle = "Done"
  234. public var emptyMessage = "No albums"
  235. public var emptyImage: UIImage? = nil
  236. public var usedCameraButton = true
  237. public var usedPrefetch = false
  238. public var previewAtForceTouch = false
  239. public var allowedLivePhotos = true
  240. public var allowedVideo = true
  241. public var allowedAlbumCloudShared = false
  242. public var allowedVideoRecording = true //for camera : allow this option when you want to recording video.
  243. public var recordingVideoQuality: UIImagePickerControllerQualityType = .typeMedium //for camera : recording video quality
  244. public var maxVideoDuration:TimeInterval? = nil //for camera : max video recording duration
  245. public var autoPlay = true
  246. public var muteAudio = true
  247. public var mediaType: PHAssetMediaType? = nil
  248. public var numberOfColumn = 3
  249. public var singleSelectedMode = false
  250. public var maxSelectedAssets: Int? = nil //default: inf
  251. public var fetchOption: PHFetchOptions? = nil //default: creationDate
  252. public var fetchCollectionOption: [FetchCollectionType: PHFetchOptions] = [:]
  253. public var singleSelectedMode = false
  254. public var selectedColor = UIColor(red: 88/255, green: 144/255, blue: 255/255, alpha: 1.0)
  255. public var cameraBgColor = UIColor(red: 221/255, green: 223/255, blue: 226/255, alpha: 1)
  256. public var cameraIcon = TLBundle.podBundleImage(named: "camera")
  257. public var videoIcon = TLBundle.podBundleImage(named: "video")
  258. public var placeholderIcon = TLBundle.podBundleImage(named: "insertPhotoMaterial")
  259. public var nibSet: (nibName: String, bundle:Bundle)? = nil // custom cell
  260. public var cameraCellNibSet: (nibName: String, bundle:Bundle)? = nil // custom camera cell
  261. public var fetchCollectionTypes: [(PHAssetCollectionType,PHAssetCollectionSubtype)]? = nil
  262. public var groupByFetch: PHFetchedResultGroupedBy? = nil // cannot be used prefetch options
  263. public var supportedInterfaceOrientations: UIInterfaceOrientationMask = .portrait
  264. public var popup: [PopupConfigure] = []
  265. public init() {
  266. }
  267. }
  268. //Related issue: https://github.com/tilltue/TLPhotoPicker/issues/201
  269. //e.g.
  270. //let option = PHFetchOptions()
  271. //configure.fetchCollectionOption[.assetCollections(.smartAlbum)] = option
  272. //configure.fetchCollectionOption[.assetCollections(.album)] = option
  273. //configure.fetchCollectionOption[.topLevelUserCollections] = option
  274. public enum FetchCollectionType {
  275. case assetCollections(PHAssetCollectionType)
  276. case topLevelUserCollections
  277. }
  278. public enum PopupConfigure {
  279. //Popup album view animation duration
  280. case animation(TimeInterval)
  281. }
  282. // PHFetchedResultGroupedBy
  283. //
  284. // CGrouped by date, cannot be used prefetch options
  285. // take about few seconds ( 5000 image iPhoneX: 1 ~ 1.5 sec )
  286. public enum PHFetchedResultGroupedBy {
  287. case year
  288. case month
  289. case week
  290. case day
  291. case hour
  292. case custom(dateFormat: String)
  293. }
  294. //customizable photos picker viewcontroller
  295. class CustomPhotoPickerViewController: TLPhotosPickerViewController {
  296. override func makeUI() {
  297. super.makeUI()
  298. self.customNavItem.leftBarButtonItem = UIBarButtonItem.init(barButtonSystemItem: .stop, target: nil, action: #selector(customAction))
  299. }
  300. func customAction() {
  301. self.dismiss(animated: true, completion: nil)
  302. }
  303. }
  304. //for log
  305. public protocol TLPhotosPickerLogDelegate: class {
  306. func selectedCameraCell(picker: TLPhotosPickerViewController)
  307. func deselectedPhoto(picker: TLPhotosPickerViewController, at: Int)
  308. func selectedPhoto(picker: TLPhotosPickerViewController, at: Int)
  309. func selectedAlbum(picker: TLPhotosPickerViewController, title: String, at: Int)
  310. }
  311. //for collection supplement view
  312. let viewController = TLPhotosPickerViewController()
  313. viewController.customDataSouces = CustomDataSources() // inherit TLPhotopickerDataSourcesProtocol
  314. public protocol TLPhotopickerDataSourcesProtocol {
  315. func headerReferenceSize() -> CGSize
  316. func footerReferenceSize() -> CGSize
  317. func registerSupplementView(collectionView: UICollectionView)
  318. func supplementIdentifier(kind: String) -> String
  319. func configure(supplement view: UICollectionReusableView, section: (title: String, assets: [TLPHAsset]))
  320. }
  321. ```
  322. ## Author
  323. Does your organization or project use TLPhotoPicker? Please let me know by email.
  324. wade.hawk, junhyi.park@gmail.com
  325. ## License
  326. TLPhotoPicker is available under the MIT license. See the LICENSE file for more info.