Refactors loading operations to inherit from Roxas’ RSTLoadOperation

This commit is contained in:
Riley Testut 2017-03-15 13:07:34 -06:00
parent 882c6e74e6
commit 54da484423
7 changed files with 116 additions and 89 deletions

View File

@ -47,7 +47,6 @@
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */; };
BF59425C1E09BB810051894B /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942581E09BB810051894B /* Action.swift */; };
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */; };
BF5942651E09BBB10051894B /* LoadImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942621E09BBB10051894B /* LoadImageOperation.swift */; };
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */; };
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */; };
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */; };
@ -153,7 +152,6 @@
BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseTransitionCoordinator.swift; path = "Pause Menu/Segues/PauseTransitionCoordinator.swift"; sourceTree = "<group>"; };
BF5942581E09BB810051894B /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Action.swift; path = Components/Action.swift; sourceTree = "<group>"; };
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadControllerSkinImageOperation.swift; path = Components/Loading/LoadControllerSkinImageOperation.swift; sourceTree = "<group>"; };
BF5942621E09BBB10051894B /* LoadImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadImageOperation.swift; path = Components/Loading/LoadImageOperation.swift; sourceTree = "<group>"; };
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadImageURLOperation.swift; path = Components/Loading/LoadImageURLOperation.swift; sourceTree = "<group>"; };
BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GridCollectionViewCell.swift; path = "Components/Collection View/GridCollectionViewCell.swift"; sourceTree = "<group>"; };
BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GridCollectionViewLayout.swift; path = "Components/Collection View/GridCollectionViewLayout.swift"; sourceTree = "<group>"; };
@ -306,9 +304,8 @@
BF5942601E09BBA80051894B /* Loading */ = {
isa = PBXGroup;
children = (
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */,
BF5942621E09BBB10051894B /* LoadImageOperation.swift */,
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */,
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */,
);
name = Loading;
sourceTree = "<group>";
@ -752,7 +749,6 @@
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
BF5942651E09BBB10051894B /* LoadImageOperation.swift in Sources */,
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
BF1173501DA32CF600047DF8 /* ControllersSettingsViewController.swift in Sources */,
BFFC461E1D59823500AF2CC6 /* GamesPresentationController.swift in Sources */,

View File

@ -10,6 +10,17 @@ import UIKit
import DeltaCore
import Roxas
extension LoadControllerSkinImageOperation
{
enum Error: Swift.Error
{
case doesNotExist
case unsupportedTraits
}
}
class ControllerSkinImageCacheKey: NSObject
{
let controllerSkin: ControllerSkin
@ -36,7 +47,7 @@ class ControllerSkinImageCacheKey: NSObject
}
}
class LoadControllerSkinImageOperation: LoadImageOperation<ControllerSkinImageCacheKey>
class LoadControllerSkinImageOperation: RSTLoadOperation<UIImage, ControllerSkinImageCacheKey>
{
let controllerSkin: ControllerSkin
let traits: DeltaCore.ControllerSkin.Traits
@ -52,9 +63,23 @@ class LoadControllerSkinImageOperation: LoadImageOperation<ControllerSkinImageCa
super.init(cacheKey: cacheKey)
}
override func loadImage() -> UIImage?
override func loadResult(completion: @escaping (UIImage?, Swift.Error?) -> Void)
{
let image = self.controllerSkin.image(for: self.traits, preferredSize: self.size)
return image
guard self.controllerSkin.supports(self.traits) else {
completion(nil, Error.unsupportedTraits)
return
}
guard let image = self.controllerSkin.image(for: self.traits, preferredSize: self.size) else {
completion(nil, Error.doesNotExist)
return
}
// Force decompression of image
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1, height: 1), true, 1.0)
image.draw(at: CGPoint.zero)
UIGraphicsEndImageContext()
completion(image, nil)
}
}

View File

@ -1,69 +0,0 @@
//
// LoadImageOperation.swift
// Delta
//
// Created by Riley Testut on 2/26/16.
// Copyright © 2016 Riley Testut. All rights reserved.
//
import Foundation
import ImageIO
import Roxas
class LoadImageOperation<CacheKeyType: AnyObject>: RSTOperation
{
var completionHandler: ((UIImage?) -> Void)? {
didSet {
self.completionBlock = {
rst_dispatch_sync_on_main_thread() {
self.completionHandler?(self.image)
}
}
}
}
var imageCache: NSCache<CacheKeyType, UIImage>? {
didSet {
// Ensures if an image is cached, it will be returned immediately, to prevent temporary flash of placeholder image
self.isImmediate = self.imageCache?.object(forKey: self.cacheKey) != nil
}
}
private let cacheKey: CacheKeyType
private var image: UIImage?
init(cacheKey: CacheKeyType)
{
self.cacheKey = cacheKey
super.init()
}
override func main()
{
guard !self.isCancelled else { return }
if let cachedImage = self.imageCache?.object(forKey: self.cacheKey)
{
self.image = cachedImage
return
}
guard let loadedImage = self.loadImage() else { return }
// Force decompression of image
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1, height: 1), true, 1.0)
loadedImage.draw(at: CGPoint.zero)
UIGraphicsEndImageContext()
self.imageCache?.setObject(loadedImage, forKey: self.cacheKey)
self.image = loadedImage
}
func loadImage() -> UIImage?
{
return nil
}
}

View File

@ -9,25 +9,100 @@
import UIKit
import ImageIO
import SDWebImage
import Roxas
class LoadImageURLOperation: LoadImageOperation<NSURL>
extension LoadImageURLOperation
{
public let url: URL
enum Error: Swift.Error
{
case doesNotExist
case invalid
case downloadFailed(Swift.Error)
}
}
class LoadImageURLOperation: RSTLoadOperation<UIImage, NSURL>
{
let url: URL
override var isAsynchronous: Bool {
return !self.url.isFileURL
}
private var downloadOperation: SDWebImageOperation?
init(url: URL)
{
self.url = url
super.init(cacheKey: url as NSURL)
}
override func loadImage() -> UIImage?
override func cancel()
{
super.cancel()
self.downloadOperation?.cancel()
}
override func loadResult(completion: @escaping (UIImage?, Swift.Error?) -> Void)
{
let callback = { (image: UIImage?, error: Error?) in
if let image = image, !self.isCancelled
{
// Force decompression of image
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1, height: 1), true, 1.0)
image.draw(at: CGPoint.zero)
UIGraphicsEndImageContext()
}
completion(image, error)
}
if self.url.isFileURL
{
self.loadLocalImage(completion: callback)
}
else
{
self.loadRemoteImage(completion: callback)
}
}
private func loadLocalImage(completion: @escaping (UIImage?, Error?) -> Void)
{
let options: NSDictionary = [kCGImageSourceShouldCache as NSString: true]
guard let imageSource = CGImageSourceCreateWithURL(self.url as CFURL, options), let quartzImage = CGImageSourceCreateImageAtIndex(imageSource, 0, options) else { return nil }
guard let imageSource = CGImageSourceCreateWithURL(self.url as CFURL, options) else {
completion(nil, .doesNotExist)
return
}
guard let quartzImage = CGImageSourceCreateImageAtIndex(imageSource, 0, options) else {
completion(nil, .invalid)
return
}
let image = UIImage(cgImage: quartzImage)
return image
completion(image, nil)
}
private func loadRemoteImage(completion: @escaping (UIImage?, Error?) -> Void)
{
let manager = SDWebImageManager.shared()
self.downloadOperation = manager?.downloadImage(with: self.url, options: [.retryFailed, .continueInBackground], progress: nil, completed: { (image, error, cacheType, finished, imageURL) in
if let error = error
{
completion(nil, .downloadFailed(error))
}
else
{
completion(image, nil)
}
})
}
}

View File

@ -236,8 +236,8 @@ private extension SaveStatesViewController
if !ignoreOperations
{
let imageOperation = LoadImageURLOperation(url: saveState.imageFileURL)
imageOperation.imageCache = self.imageCache
imageOperation.completionHandler = { image in
imageOperation.resultsCache = self.imageCache
imageOperation.resultHandler = { (image, error) in
if let image = image
{

View File

@ -95,8 +95,8 @@ private extension ControllerSkinsViewController
let size = UIScreen.main.defaultControllerSkinSize
let imageOperation = LoadControllerSkinImageOperation(controllerSkin: controllerSkin, traits: self.traits, size: size)
imageOperation.imageCache = self.imageCache
imageOperation.completionHandler = { image in
imageOperation.resultsCache = self.imageCache
imageOperation.resultHandler = { (image, error) in
guard let image = image else { return }
@ -159,7 +159,7 @@ extension ControllerSkinsViewController: UITableViewDataSourcePrefetching
let size = UIScreen.main.defaultControllerSkinSize
let imageOperation = LoadControllerSkinImageOperation(controllerSkin: controllerSkin, traits: self.traits, size: size)
imageOperation.imageCache = self.imageCache
imageOperation.resultsCache = self.imageCache
self.imageOperationQueue.addOperation(imageOperation, forKey: indexPath as NSCopying)
}

2
External/Roxas vendored

@ -1 +1 @@
Subproject commit ecb29e0050fe9335845698c896c4cd8ce60df0da
Subproject commit 0057bde6337f90b5747eec1baf94a1360e78c23e