PPAsset.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import Foundation
  2. import AVFoundation
  3. import UIKit
  4. import Photos
  5. /**
  6. Protocol representation of types of media that asset can provide.
  7. Image assets will return full image as an image and nil video.
  8. Video assets will return thumbnail as an image and AVAsset as video.
  9. */
  10. public protocol MediaProvider {
  11. func image() -> UIImage?
  12. func video() -> AVAsset?
  13. func phasset() -> PHAsset?
  14. }
  15. /**
  16. Protocol representation of an asset.
  17. */
  18. protocol PPAsset: MediaProvider {
  19. associatedtype AssetType
  20. var asset: AssetType { get }
  21. }
  22. /**
  23. Default implementations of MediaProviding protocol methods.
  24. */
  25. extension PPAsset {
  26. public func image() -> UIImage? {
  27. var result: UIImage?
  28. if let image = asset as? UIImage {
  29. result = image
  30. } else if let avasset = asset as? AVAsset {
  31. let imageGenerator = AVAssetImageGenerator(asset: avasset)
  32. imageGenerator.appliesPreferredTrackTransform = true
  33. var time = avasset.duration
  34. time.value = min(time.value, 1)
  35. if let imageRef = try? imageGenerator.copyCGImage(at: time, actualTime: nil) {
  36. result = UIImage(cgImage: imageRef)
  37. }
  38. }
  39. return result
  40. }
  41. public func video() -> AVAsset? {
  42. var result: AVAsset?
  43. if let avasset = asset as? AVAsset {
  44. result = avasset
  45. }
  46. return result
  47. }
  48. public func phasset() -> PHAsset? {
  49. var result: PHAsset?
  50. if let pa = asset as? PHAsset {
  51. result = pa
  52. }
  53. return result
  54. }
  55. public func phassetIsImage() -> Bool {
  56. if let asset = self.phasset() {
  57. if asset.mediaType == .image {
  58. return true
  59. }
  60. }
  61. return false
  62. }
  63. }
  64. /**
  65. Conforming asset types.
  66. */
  67. extension UIImage: PPAsset {
  68. internal var asset: UIImage {
  69. return self
  70. }
  71. func resizedImage(newSize: CGSize) -> UIImage {
  72. // Guard newSize is different
  73. guard self.size != newSize else { return self }
  74. UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
  75. self.draw(in: CGRect(x: 0.0, y: 0.0, width: newSize.width, height: newSize.height))
  76. let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
  77. UIGraphicsEndImageContext()
  78. return newImage
  79. }
  80. func resizedImageWithinRect(rectSize: CGSize) -> UIImage {
  81. let widthFactor = size.width / rectSize.width
  82. let heightFactor = size.height / rectSize.height
  83. var resizeFactor = widthFactor
  84. if size.height > size.width {
  85. resizeFactor = heightFactor
  86. }
  87. let newSize = CGSize(width: size.width/resizeFactor, height: size.height/resizeFactor)
  88. let resized = resizedImage(newSize: newSize)
  89. return resized
  90. }
  91. }
  92. extension AVAsset: PPAsset {
  93. internal var asset: AVAsset {
  94. return self
  95. }
  96. }
  97. extension PHAsset: PPAsset {
  98. internal var asset: PHAsset {
  99. return self
  100. }
  101. func fetchThumbnail(_ size: CGSize, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  102. PHCachingImageManager().requestImage(for: asset, targetSize: CGSize(width: 220.0, height: 220.0), contentMode: .aspectFit, options: nil, resultHandler: { (image, dict) in
  103. completeBlock(image, dict)
  104. })
  105. }
  106. func fetchImageForAsset(size: CGSize, options: PHImageRequestOptions?, contentMode: PHImageContentMode, completeBlock: @escaping (_ image: UIImage?, _ info: [AnyHashable: Any]?) -> Void) {
  107. PHCachingImageManager().requestImage(for: asset, targetSize: size, contentMode: contentMode, options: options, resultHandler: { image, info in
  108. if let isInCloud = info?[PHImageResultIsInCloudKey] as AnyObject?
  109. , image == nil && isInCloud.boolValue {
  110. options?.isNetworkAccessAllowed = true
  111. self.fetchImageForAsset(size: size, options: options, contentMode: contentMode, completeBlock: completeBlock)
  112. } else {
  113. completeBlock(image, info)
  114. }
  115. })
  116. }
  117. }
  118. /**
  119. Type erasing wrapper around PPAsset.
  120. Covariance is not supported as of Swift 3.
  121. */
  122. struct AnyPPAsset<AssetType>: PPAsset {
  123. let base: AssetType
  124. init<A : PPAsset>(_ base: A) where A.AssetType == AssetType {
  125. self.base = base.asset
  126. }
  127. internal var asset: AssetType {
  128. return base
  129. }
  130. }