DKAsset.swift 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //
  2. // DKAsset.swift
  3. // DKImagePickerControllerDemo
  4. //
  5. // Created by ZhangAo on 15/12/13.
  6. // Copyright © 2015年 ZhangAo. All rights reserved.
  7. //
  8. import Photos
  9. public extension CGSize {
  10. public func toPixel() -> CGSize {
  11. let scale = UIScreen.main.scale
  12. return CGSize(width: self.width * scale, height: self.height * scale)
  13. }
  14. }
  15. /**
  16. An `DKAsset` object represents a photo or a video managed by the `DKImagePickerController`.
  17. */
  18. open class DKAsset: NSObject {
  19. /// Returns a UIImage that is appropriate for displaying full screen.
  20. private var fullScreenImage: (image: UIImage?, info: [AnyHashable: Any]?)?
  21. /// When the asset was an image, it's false. Otherwise true.
  22. @objc open private(set) var isVideo: Bool = false
  23. /// Returns location, if its contained in original asser
  24. open private(set) var location: CLLocation?
  25. /// play time duration(seconds) of a video.
  26. open private(set) var duration: Double?
  27. @objc open private(set) var originalAsset: PHAsset?
  28. open var localIdentifier: String
  29. public init(originalAsset: PHAsset) {
  30. self.localIdentifier = originalAsset.localIdentifier
  31. self.location = originalAsset.location
  32. super.init()
  33. self.originalAsset = originalAsset
  34. let assetType = originalAsset.mediaType
  35. if assetType == .video {
  36. self.isVideo = true
  37. self.duration = originalAsset.duration
  38. }
  39. }
  40. private var image: UIImage?
  41. internal init(image: UIImage) {
  42. self.localIdentifier = String(image.hash)
  43. super.init()
  44. self.image = image
  45. self.fullScreenImage = (image, nil)
  46. }
  47. override open func isEqual(_ object: Any?) -> Bool {
  48. if let another = object as? DKAsset {
  49. return self.localIdentifier == another.localIdentifier
  50. }
  51. return false
  52. }
  53. public func fetchImageWithSize(_ size: CGSize, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  54. self.fetchImageWithSize(size, options: nil, completeBlock: completeBlock)
  55. }
  56. public func fetchImageWithSize(_ size: CGSize, options: PHImageRequestOptions?, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  57. self.fetchImageWithSize(size, options: options, contentMode: .aspectFit, completeBlock: completeBlock)
  58. }
  59. public func fetchImageWithSize(_ size: CGSize, options: PHImageRequestOptions?, contentMode: PHImageContentMode, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  60. if let _ = self.originalAsset {
  61. getImageManager().fetchImageForAsset(self, size: size, options: options, contentMode: contentMode, completeBlock: completeBlock)
  62. } else {
  63. completeBlock(self.image, nil)
  64. }
  65. }
  66. public func fetchFullScreenImageWithCompleteBlock(_ completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  67. self.fetchFullScreenImage(false, completeBlock: completeBlock)
  68. }
  69. /**
  70. Fetch an image with the current screen size.
  71. - parameter sync: If true, the method blocks the calling thread until image is ready or an error occurs.
  72. - parameter completeBlock: The block is executed when the image download is complete.
  73. */
  74. public func fetchFullScreenImage(_ sync: Bool, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  75. if let (image, info) = self.fullScreenImage {
  76. completeBlock(image, info)
  77. } else {
  78. let screenSize = UIScreen.main.bounds.size
  79. let options = PHImageRequestOptions()
  80. options.deliveryMode = .highQualityFormat
  81. options.resizeMode = .exact
  82. options.isSynchronous = sync
  83. getImageManager().fetchImageForAsset(self, size: screenSize.toPixel(), options: options, contentMode: .aspectFit) { [weak self] image, info in
  84. guard let strongSelf = self else { return }
  85. strongSelf.fullScreenImage = (image, info)
  86. completeBlock(image, info)
  87. }
  88. }
  89. }
  90. public func fetchOriginalImageWithCompleteBlock(_ completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  91. self.fetchOriginalImage(false, completeBlock: completeBlock)
  92. }
  93. /**
  94. Fetch an image with the original size.
  95. - parameter sync: If true, the method blocks the calling thread until image is ready or an error occurs.
  96. - parameter completeBlock: The block is executed when the image download is complete.
  97. */
  98. public func fetchOriginalImage(_ sync: Bool, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  99. let options = PHImageRequestOptions()
  100. options.version = .current
  101. options.isSynchronous = sync
  102. getImageManager().fetchImageDataForAsset(self, options: options, completeBlock: { (data, info) in
  103. var image: UIImage?
  104. if let data = data {
  105. image = UIImage(data: data)
  106. }
  107. completeBlock(image, info)
  108. })
  109. }
  110. /**
  111. Fetch an image data with the original size.
  112. - parameter sync: If true, the method blocks the calling thread until image is ready or an error occurs.
  113. - parameter completeBlock: The block is executed when the image download is complete.
  114. */
  115. public func fetchImageDataForAsset(_ sync: Bool, completeBlock: @escaping (_ imageData: Data?, _ info: [AnyHashable: Any]?) -> Void) {
  116. let options = PHImageRequestOptions()
  117. options.version = .current
  118. options.isSynchronous = sync
  119. getImageManager().fetchImageDataForAsset(self, options: options, completeBlock: { (data, info) in
  120. completeBlock(data, info)
  121. })
  122. }
  123. /**
  124. Fetch an AVAsset with a completeBlock.
  125. */
  126. public func fetchAVAssetWithCompleteBlock(_ completeBlock: @escaping (_ AVAsset: AVAsset?, _ info: [AnyHashable: Any]?) -> Void) {
  127. self.fetchAVAsset(nil, completeBlock: completeBlock)
  128. }
  129. /**
  130. Fetch an AVAsset with a completeBlock and PHVideoRequestOptions.
  131. */
  132. public func fetchAVAsset(_ options: PHVideoRequestOptions?, completeBlock: @escaping (_ AVAsset: AVAsset?, _ info: [AnyHashable: Any]?) -> Void) {
  133. getImageManager().fetchAVAsset(self, options: options, completeBlock: completeBlock)
  134. }
  135. /**
  136. Sync fetch an AVAsset with a completeBlock and PHVideoRequestOptions.
  137. */
  138. public func fetchAVAsset(_ sync: Bool, options: PHVideoRequestOptions?, completeBlock: @escaping (_ AVAsset: AVAsset?, _ info: [AnyHashable: Any]?) -> Void) {
  139. if sync {
  140. let semaphore = DispatchSemaphore(value: 0)
  141. self.fetchAVAsset(options, completeBlock: { (AVAsset, info) -> Void in
  142. completeBlock(AVAsset, info)
  143. semaphore.signal()
  144. })
  145. _ = semaphore.wait(timeout: DispatchTime.distantFuture)
  146. } else {
  147. self.fetchAVAsset(options, completeBlock: completeBlock)
  148. }
  149. }
  150. }
  151. public extension DKAsset {
  152. struct DKAssetWriter {
  153. static let writeQueue: OperationQueue = {
  154. let queue = OperationQueue()
  155. queue.name = "DKAsset_Write_Queue"
  156. queue.maxConcurrentOperationCount = 5
  157. return queue
  158. }()
  159. }
  160. /**
  161. Writes the image in the receiver to the file specified by a given path.
  162. */
  163. public func writeImageToFile(_ path: String, completeBlock: @escaping (_ success: Bool) -> Void) {
  164. let options = PHImageRequestOptions()
  165. options.version = .current
  166. getImageManager().fetchImageDataForAsset(self, options: options, completeBlock: { (data, _) in
  167. DKAssetWriter.writeQueue.addOperation({
  168. if let imageData = data {
  169. try? imageData.write(to: URL(fileURLWithPath: path), options: [.atomic])
  170. completeBlock(true)
  171. } else {
  172. completeBlock(false)
  173. }
  174. })
  175. })
  176. }
  177. /**
  178. Writes the AV in the receiver to the file specified by a given path.
  179. - parameter presetName: An NSString specifying the name of the preset template for the export. See AVAssetExportPresetXXX.
  180. */
  181. public func writeAVToFile(_ path: String, presetName: String, completeBlock: @escaping (_ success: Bool) -> Void) {
  182. self.fetchAVAsset(nil) { (avAsset, _) in
  183. DKAssetWriter.writeQueue.addOperation({
  184. if let avAsset = avAsset,
  185. let exportSession = AVAssetExportSession(asset: avAsset, presetName: presetName) {
  186. exportSession.outputFileType = AVFileType.mov
  187. exportSession.outputURL = URL(fileURLWithPath: path)
  188. exportSession.shouldOptimizeForNetworkUse = true
  189. exportSession.exportAsynchronously(completionHandler: {
  190. completeBlock(exportSession.status == .completed ? true : false)
  191. })
  192. } else {
  193. completeBlock(false)
  194. }
  195. })
  196. }
  197. }
  198. }
  199. public extension AVAsset {
  200. public func calculateFileSize() -> Float {
  201. if let URLAsset = self as? AVURLAsset {
  202. var size: AnyObject?
  203. try! (URLAsset.url as NSURL).getResourceValue(&size, forKey: URLResourceKey.fileSizeKey)
  204. if let size = size as? NSNumber {
  205. return size.floatValue
  206. } else {
  207. return 0
  208. }
  209. } else if let _ = self as? AVComposition {
  210. var estimatedSize: Float = 0.0
  211. var duration: Float = 0.0
  212. for track in self.tracks {
  213. let rate = track.estimatedDataRate / 8.0
  214. let seconds = Float(CMTimeGetSeconds(track.timeRange.duration))
  215. duration += seconds
  216. estimatedSize += seconds * rate
  217. }
  218. return estimatedSize
  219. } else {
  220. return 0
  221. }
  222. }
  223. }