Last active
May 16, 2017 00:53
-
-
Save timusus/ac3af8baf4ecdd2ad41a2c6a0eecf9df to your computer and use it in GitHub Desktop.
Library-agnostic image loading (iOS/Swift)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| class HanekeImageLoader: ImageLoader { | |
| /** | |
| Haneke requires that we register our 'formats' before they can be used. | |
| **/ | |
| func setup() { | |
| ImageTransformationSet.imageTransformationSets.forEach { imageTransformationSet in | |
| if let format = createFormat(for: imageTransformationSet) { | |
| Shared.imageCache.addFormat(format) | |
| } | |
| } | |
| HanekeGlobals.UIKit.SetImageAnimationDuration = 0.4 | |
| } | |
| func loadImageFromURL(imageView: UIImageView, url: URL, placeholder: UIImage? = nil, imageTransformationSet: ImageTransformationSet? = nil) { | |
| imageView.hnk_setImageFromURL(url, placeholder: placeholder, format: getFormat(for: imageTransformationSet), failure: nil, success: nil) | |
| } | |
| func cancelImageLoad(imageView: UIImageView) { | |
| imageView.hnk_cancelSetImage() | |
| } | |
| func createFormat(for imageTransitionSet: ImageTransformationSet) -> Format<UIImage>? { | |
| return Format<UIImage>(name: imageTransitionSet.name, diskCapacity: UInt64(imageTransitionSet.cacheSize * 1024 * 1024)) { image in | |
| return self.applyTransformations(for: imageTransitionSet, image: image) | |
| } | |
| } | |
| func getFormat(for imageTransformationSet: ImageTransformationSet?) -> Format<UIImage>? { | |
| guard let imageTransformationSet = imageTransformationSet else { | |
| return nil | |
| } | |
| if let (format, _, _) = Shared.imageCache.formats[imageTransformationSet.name] { | |
| return format | |
| } | |
| return nil | |
| } | |
| func applyTransformations(for imageTransformationSet: ImageTransformationSet, image: UIImage) -> UIImage { | |
| var transformedImage = image | |
| imageTransformationSet.imageTransformations.forEach { transformation in | |
| switch transformation { | |
| case .crop: | |
| if transformedImage.size.width != transformedImage.size.height { | |
| let smallestLength = min(transformedImage.size.width, transformedImage.size.height) | |
| transformedImage = transformedImage.imageCropped(to: CGRect(x: 0, y: 0, width: smallestLength, height: smallestLength))! | |
| } | |
| break | |
| case .scale(let size): | |
| transformedImage = transformedImage.imageScaled(toMaxSize: size) | |
| break | |
| case .blur(let radius): | |
| transformedImage = UIImageEffects.blurImage(transformedImage, radius: radius) | |
| break | |
| case .round(let radius): | |
| transformedImage = transformedImage.imageRounded(radius: radius) | |
| break | |
| } | |
| } | |
| return transformedImage | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| protocol ImageLoader { | |
| /** | |
| Opportunity to do any initial setup for the ImageLoader. | |
| Typically, this would be called in `AppDelegate.didFinishLaunchingWithOptions()` | |
| **/ | |
| func setup() | |
| func loadImageFromURL(imageView: UIImageView, url: URL, placeholder: UIImage?, imageTransformationSet: ImageTransformationSet?) | |
| func cancelImageLoad(imageView: UIImageView) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| enum ImageTransformation { | |
| case blur(level: CGFloat) | |
| case scale(size: CGSize) | |
| case crop() | |
| case round(radius: CGFloat) | |
| } | |
| class ImageTransformationSet { | |
| let name: String | |
| let imageTransformations: [ImageTransformation] | |
| let cacheSize: Int | |
| init(name: String, imageTransformations: [ImageTransformation], cacheSize: Int) { | |
| self.name = name | |
| self.imageTransformations = imageTransformations | |
| self.cacheSize = cacheSize | |
| } | |
| static let avatarSmall = ImageTransformationSet(name: "avatarSmall", imageTransformations: [ | |
| // ImageTransformation.crop(), | |
| // ImageTransformation.scale(size: CGSize(width: 36, height: 36)), | |
| // ImageTransformation.round(radius: AVATAR_SMALL_CORNER_RADIUS) | |
| ], cacheSize: 4) | |
| static let avatarMedium = ImageTransformationSet(name: "avatarMedium", imageTransformations: [ | |
| // ImageTransformation.crop(), | |
| // ImageTransformation.scale(size: CGSize(width: 65, height: 65)), | |
| // ImageTransformation.round(radius: AVATAR_MEDIUM_CORNER_RADIUS) | |
| ], cacheSize: 6) | |
| static let inlineImages = ImageTransformationSet(name: "inlineImages", imageTransformations: [ | |
| ImageTransformation.scale(size: CGSize(width: UIScreen.main.nativeBounds.width, height: UIScreen.main.nativeBounds.width)), | |
| ], cacheSize: 60) | |
| static let blur = ImageTransformationSet(name: "blurImages", imageTransformations: [ | |
| ImageTransformation.blur(level: UIImageEffects.BlurLevel.ultraLow.rawValue) | |
| ], cacheSize: 6) | |
| static let imageTransformationSets: [ImageTransformationSet] = [ | |
| ImageTransformationSet.avatarSmall, | |
| ImageTransformationSet.avatarMedium, | |
| ImageTransformationSet.inlineImages, | |
| ImageTransformationSet.blur | |
| ] | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Kingfisher | |
| class KingfisherImageLoader: ImageLoader { | |
| func setup() { | |
| //Set maximum cache size of 60MB | |
| ImageCache.default.maxDiskCacheSize = 60 * 1024 * 1024 | |
| } | |
| func loadImageFromURL(imageView: UIImageView, url: URL, placeholder: UIImage?, imageTransformationSet: ImageTransformationSet?) { | |
| imageView.kf.setImage(with: url, placeholder: placeholder, options: getOptions(for: imageTransformationSet)) | |
| } | |
| func cancelImageLoad(imageView: UIImageView) { | |
| imageView.kf.cancelDownloadTask() | |
| } | |
| func getOptions(for imageTransformationSet: ImageTransformationSet?) -> KingfisherOptionsInfo? { | |
| var options: KingfisherOptionsInfo = [ | |
| .transition(.fade(0.4)), | |
| .backgroundDecode, | |
| .callbackDispatchQueue(DispatchQueue.global(qos: DispatchQoS.QoSClass.utility)) | |
| ] | |
| guard let imageTransformationSet = imageTransformationSet else { | |
| return options | |
| } | |
| var processor: ImageProcessor? = nil | |
| imageTransformationSet.imageTransformations.forEach { transformation in | |
| var newProcessor: ImageProcessor? = nil | |
| switch transformation { | |
| case .crop(): | |
| newProcessor = SquareCropProcessor() | |
| break | |
| case .scale(let size): | |
| newProcessor = ResizingImageProcessor(referenceSize: size) | |
| break | |
| case .blur(let radius): | |
| newProcessor = BlurImageProcessor(blurRadius: radius) | |
| break | |
| case .round(let radius): | |
| newProcessor = RoundCornerImageProcessor(cornerRadius: radius) | |
| break | |
| } | |
| if (newProcessor != nil) { | |
| if (processor != nil) { | |
| processor = processor!.append(another: newProcessor!) | |
| } else { | |
| processor = newProcessor | |
| } | |
| } | |
| } | |
| if let processor = processor { | |
| options += [.processor(processor)]//, .scaleFactor(UIScreen.main.scale)] | |
| } | |
| return options | |
| } | |
| class SquareCropProcessor: ImageProcessor { | |
| let identifier: String = "squarecropprocessor" | |
| func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { | |
| switch item { | |
| case .image(let image): | |
| if image.size.width != image.size.height { | |
| let smallestLength = min(image.size.width, image.size.height) | |
| return image.imageCropped(to: CGRect(x: 0, y: 0, width: smallestLength, height: smallestLength))! | |
| } | |
| return image | |
| default: | |
| return (DefaultImageProcessor.default >> self).process(item: item, options: options) | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| extension UIImageView { | |
| func setImageFromURL(url: URL, placeholder: UIImage? = nil, imageTransformationSet: ImageTransformationSet? = nil) { | |
| ImageLoaderManager.sharedInstance().imageLoader.loadImageFromURL(imageView: self, url: url, placeholder: placeholder, imageTransformationSet: imageTransformationSet) | |
| } | |
| func prepareForReuse() { | |
| ImageLoaderManager.sharedInstance().imageLoader.cancelImageLoad(imageView: self) | |
| self.image = nil | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment