forked from shadowfacts/Tusker
Cache UIImage objects to avoid re-decoding images unnecessarily
This commit is contained in:
parent
27b39b79e6
commit
c12d2db258
|
@ -112,6 +112,7 @@
|
||||||
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */; };
|
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */; };
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
||||||
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
||||||
|
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6311C4F25B3765B00B27539 /* ImageDataCache.swift */; };
|
||||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
||||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
||||||
D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; };
|
D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; };
|
||||||
|
@ -468,6 +469,7 @@
|
||||||
D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = "<group>"; };
|
D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
||||||
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
||||||
|
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataCache.swift; sourceTree = "<group>"; };
|
||||||
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
||||||
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1493,6 +1495,7 @@
|
||||||
children = (
|
children = (
|
||||||
D6F1F84C2193B56E00F5FE67 /* Cache.swift */,
|
D6F1F84C2193B56E00F5FE67 /* Cache.swift */,
|
||||||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */,
|
04DACE8D212CC7CC009840C4 /* ImageCache.swift */,
|
||||||
|
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */,
|
||||||
);
|
);
|
||||||
path = Caching;
|
path = Caching;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1889,6 +1892,7 @@
|
||||||
D627943723A552C200D38C68 /* BookmarkStatusActivity.swift in Sources */,
|
D627943723A552C200D38C68 /* BookmarkStatusActivity.swift in Sources */,
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
||||||
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */,
|
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */,
|
||||||
|
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */,
|
||||||
D6BC9DD7232D7811002CA326 /* TimelinesPageViewController.swift in Sources */,
|
D6BC9DD7232D7811002CA326 /* TimelinesPageViewController.swift in Sources */,
|
||||||
D60E2F2E244248BF005F8713 /* MastodonCachePersistentStore.swift in Sources */,
|
D60E2F2E244248BF005F8713 /* MastodonCachePersistentStore.swift in Sources */,
|
||||||
D620483623D38075008A63EF /* ContentTextView.swift in Sources */,
|
D620483623D38075008A63EF /* ContentTextView.swift in Sources */,
|
||||||
|
|
|
@ -29,7 +29,7 @@ class AccountActivityItemSource: NSObject, UIActivityItemSource {
|
||||||
metadata.originalURL = account.url
|
metadata.originalURL = account.url
|
||||||
metadata.url = account.url
|
metadata.url = account.url
|
||||||
metadata.title = "\(account.displayName) (@\(account.username)@\(account.url.host!)"
|
metadata.title = "\(account.displayName) (@\(account.username)@\(account.url.host!)"
|
||||||
if let data = ImageCache.avatars.get(account.avatar),
|
if let data = ImageCache.avatars.getData(account.avatar),
|
||||||
let image = UIImage(data: data) {
|
let image = UIImage(data: data) {
|
||||||
metadata.iconProvider = NSItemProvider(object: image)
|
metadata.iconProvider = NSItemProvider(object: image)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class StatusActivityItemSource: NSObject, UIActivityItemSource {
|
||||||
let doc = try! SwiftSoup.parse(status.content)
|
let doc = try! SwiftSoup.parse(status.content)
|
||||||
let content = try! doc.text()
|
let content = try! doc.text()
|
||||||
metadata.title = "\(status.account.displayName): \"\(content)\""
|
metadata.title = "\(status.account.displayName): \"\(content)\""
|
||||||
if let data = ImageCache.avatars.get(status.account.avatar),
|
if let data = ImageCache.avatars.getData(status.account.avatar),
|
||||||
let image = UIImage(data: data) {
|
let image = UIImage(data: data) {
|
||||||
metadata.iconProvider = NSItemProvider(object: image)
|
metadata.iconProvider = NSItemProvider(object: image)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,47 +22,36 @@ class ImageCache {
|
||||||
private static let disableCaching = false
|
private static let disableCaching = false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private let cache: Cache<Data>
|
private let cache: ImageDataCache
|
||||||
|
|
||||||
private var groups = MultiThreadDictionary<URL, RequestGroup>(name: "ImageCache request groups")
|
private var groups = MultiThreadDictionary<URL, RequestGroup>(name: "ImageCache request groups")
|
||||||
|
|
||||||
private var backgroundQueue = DispatchQueue(label: "ImageCache completion queue", qos: .default)
|
private var backgroundQueue = DispatchQueue(label: "ImageCache completion queue", qos: .default)
|
||||||
|
|
||||||
init(name: String, memoryExpiry expiry: Expiry) {
|
init(name: String, memoryExpiry: Expiry, diskExpiry: Expiry? = nil) {
|
||||||
let storage = MemoryStorage<Data>(config: MemoryConfig(expiry: expiry))
|
self.cache = ImageDataCache(name: name, memoryExpiry: memoryExpiry, diskExpiry: diskExpiry)
|
||||||
self.cache = .memory(storage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(name: String, diskExpiry expiry: Expiry) {
|
func get(_ url: URL, completion: ((Data?, UIImage?) -> Void)?) -> Request? {
|
||||||
let storage = try! DiskStorage<Data>(config: DiskConfig(name: name, expiry: expiry), transformer: TransformerFactory.forData())
|
|
||||||
self.cache = .disk(storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(name: String, memoryExpiry: Expiry, diskExpiry: Expiry) {
|
|
||||||
let memory = MemoryStorage<Data>(config: MemoryConfig(expiry: memoryExpiry))
|
|
||||||
let disk = try! DiskStorage<Data>(config: DiskConfig(name: name, expiry: diskExpiry), transformer: TransformerFactory.forData())
|
|
||||||
self.cache = .hybrid(HybridStorage(memoryStorage: memory, diskStorage: disk))
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(_ url: URL, completion: ((Data?) -> Void)?) -> Request? {
|
|
||||||
let key = url.absoluteString
|
let key = url.absoluteString
|
||||||
if !ImageCache.disableCaching,
|
if !ImageCache.disableCaching,
|
||||||
// todo: calling object(forKey: key) does disk I/O and this method is often called from the main thread
|
// todo: calling object(forKey: key) does disk I/O and this method is often called from the main thread
|
||||||
// in performance sensitive paths. a nice optimization to DiskStorage would be adding an internal cache
|
// in performance sensitive paths. a nice optimization to DiskStorage would be adding an internal cache
|
||||||
// of the state (unknown/exists/does not exist) of whether or not objects exist on disk so that the slow, disk I/O
|
// of the state (unknown/exists/does not exist) of whether or not objects exist on disk so that the slow, disk I/O
|
||||||
// path can be avoided most of the time
|
// path can be avoided most of the time
|
||||||
let data = try? cache.object(forKey: key) {
|
let (data, image) = try? cache.get(key) {
|
||||||
backgroundQueue.async {
|
backgroundQueue.async {
|
||||||
completion?(data)
|
completion?(data, image)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
if let completion = completion, let group = groups[url] {
|
if let completion = completion, let group = groups[url] {
|
||||||
return group.addCallback(completion)
|
return group.addCallback(completion)
|
||||||
} else {
|
} else {
|
||||||
let group = RequestGroup(url: url) { (data) in
|
let group = RequestGroup(url: url) { (data, image) in
|
||||||
if let data = data {
|
if let data = data,
|
||||||
try? self.cache.setObject(data, forKey: key)
|
let image = UIImage(data: data) {
|
||||||
|
try? self.cache.set(key, data: data, image: image)
|
||||||
}
|
}
|
||||||
self.groups.removeValueWithoutReturning(forKey: url)
|
self.groups.removeValueWithoutReturning(forKey: url)
|
||||||
}
|
}
|
||||||
|
@ -74,8 +63,12 @@ class ImageCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(_ url: URL) -> Data? {
|
func getData(_ url: URL) -> Data? {
|
||||||
return try? cache.object(forKey: url.absoluteString)
|
return try? cache.getData(url.absoluteString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(_ url: URL) -> (Data, UIImage)? {
|
||||||
|
return try? cache.get(url.absoluteString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelWithoutCallback(_ url: URL) {
|
func cancelWithoutCallback(_ url: URL) {
|
||||||
|
@ -88,11 +81,11 @@ class ImageCache {
|
||||||
|
|
||||||
private class RequestGroup {
|
private class RequestGroup {
|
||||||
let url: URL
|
let url: URL
|
||||||
private let onFinished: (Data?) -> Void
|
private let onFinished: (Data?, UIImage?) -> Void
|
||||||
private var task: URLSessionDataTask?
|
private var task: URLSessionDataTask?
|
||||||
private var requests = [Request]()
|
private var requests = [Request]()
|
||||||
|
|
||||||
init(url: URL, onFinished: @escaping (Data?) -> Void) {
|
init(url: URL, onFinished: @escaping (Data?, UIImage?) -> Void) {
|
||||||
self.url = url
|
self.url = url
|
||||||
self.onFinished = onFinished
|
self.onFinished = onFinished
|
||||||
}
|
}
|
||||||
|
@ -116,7 +109,7 @@ class ImageCache {
|
||||||
task?.priority = max(1.0, URLSessionTask.defaultPriority + 0.1 * Float(requests.filter { !$0.cancelled }.count))
|
task?.priority = max(1.0, URLSessionTask.defaultPriority + 0.1 * Float(requests.filter { !$0.cancelled }.count))
|
||||||
}
|
}
|
||||||
|
|
||||||
func addCallback(_ completion: ((Data?) -> Void)?) -> Request {
|
func addCallback(_ completion: ((Data?, UIImage?) -> Void)?) -> Request {
|
||||||
let request = Request(callback: completion)
|
let request = Request(callback: completion)
|
||||||
requests.append(request)
|
requests.append(request)
|
||||||
updatePriority()
|
updatePriority()
|
||||||
|
@ -141,21 +134,24 @@ class ImageCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
func complete(with data: Data?) {
|
func complete(with data: Data?) {
|
||||||
|
let image = data != nil ? UIImage(data: data!) : nil
|
||||||
|
|
||||||
requests.filter { !$0.cancelled }.forEach {
|
requests.filter { !$0.cancelled }.forEach {
|
||||||
if let callback = $0.callback {
|
if let callback = $0.callback {
|
||||||
callback(data)
|
callback(data, image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.onFinished(data)
|
|
||||||
|
self.onFinished(data, image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
private weak var group: RequestGroup?
|
private weak var group: RequestGroup?
|
||||||
private(set) var callback: ((Data?) -> Void)?
|
private(set) var callback: ((Data?, UIImage?) -> Void)?
|
||||||
private(set) var cancelled: Bool = false
|
private(set) var cancelled: Bool = false
|
||||||
|
|
||||||
init(callback: ((Data?) -> Void)?) {
|
init(callback: ((Data?, UIImage?) -> Void)?) {
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// ImageDataCache.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/16/21.
|
||||||
|
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Cache
|
||||||
|
|
||||||
|
class ImageDataCache {
|
||||||
|
|
||||||
|
private let memory: MemoryStorage<(Data, UIImage)>
|
||||||
|
private let disk: DiskStorage<Data>?
|
||||||
|
|
||||||
|
init(name: String, memoryExpiry: Expiry, diskExpiry: Expiry?) {
|
||||||
|
let memoryConfig = MemoryConfig(expiry: memoryExpiry)
|
||||||
|
self.memory = MemoryStorage(config: memoryConfig)
|
||||||
|
|
||||||
|
if let diskExpiry = diskExpiry {
|
||||||
|
let diskConfig = DiskConfig(name: name, expiry: diskExpiry)
|
||||||
|
self.disk = try! DiskStorage(config: diskConfig, transformer: TransformerFactory.forData())
|
||||||
|
} else {
|
||||||
|
self.disk = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func has(_ key: String) throws -> Bool {
|
||||||
|
if try memory.existsObject(forKey: key) {
|
||||||
|
return true
|
||||||
|
} else if let disk = self.disk,
|
||||||
|
try disk.existsObject(forKey: key) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(_ key: String) throws -> (Data, UIImage)? {
|
||||||
|
if try memory.existsObject(forKey: key) {
|
||||||
|
return try! memory.object(forKey: key)
|
||||||
|
} else if let disk = self.disk,
|
||||||
|
try disk.existsObject(forKey: key),
|
||||||
|
let data = try? disk.object(forKey: key),
|
||||||
|
let image = UIImage(data: data) {
|
||||||
|
return (data, image)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImage(_ key: String) throws -> UIImage? {
|
||||||
|
return try get(key)?.1
|
||||||
|
}
|
||||||
|
|
||||||
|
func getData(_ key: String) throws -> Data? {
|
||||||
|
return try get(key)?.0
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(_ key: String, data: Data, image: UIImage) throws {
|
||||||
|
memory.setObject((data, image), forKey: key)
|
||||||
|
|
||||||
|
if let disk = self.disk {
|
||||||
|
try disk.setObject(data, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeAll() throws {
|
||||||
|
memory.removeAll()
|
||||||
|
try? disk?.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,6 +14,15 @@ struct ImageGrayscalifier {
|
||||||
private static let context = CIContext()
|
private static let context = CIContext()
|
||||||
private static let cache = NSCache<NSURL, UIImage>()
|
private static let cache = NSCache<NSURL, UIImage>()
|
||||||
|
|
||||||
|
static func convertIfNecessary(url: URL?, image: UIImage) -> UIImage? {
|
||||||
|
if Preferences.shared.grayscaleImages,
|
||||||
|
let source = image.cgImage {
|
||||||
|
return convert(url: url, cgImage: source)
|
||||||
|
} else {
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static func convert(url: URL?, data: Data) -> UIImage? {
|
static func convert(url: URL?, data: Data) -> UIImage? {
|
||||||
if let url = url,
|
if let url = url,
|
||||||
let cached = cache.object(forKey: url as NSURL) {
|
let cached = cache.object(forKey: url as NSURL) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ class AttachmentPreviewViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadView() {
|
override func loadView() {
|
||||||
if let data = ImageCache.attachments.get(attachment.url),
|
if let data = ImageCache.attachments.getData(attachment.url),
|
||||||
let image = UIImage(data: data) {
|
let image = UIImage(data: data) {
|
||||||
let imageView: UIImageView
|
let imageView: UIImageView
|
||||||
if attachment.url.pathExtension == "gif" {
|
if attachment.url.pathExtension == "gif" {
|
||||||
|
|
|
@ -44,7 +44,7 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
|
||||||
var animationGifData: Data? {
|
var animationGifData: Data? {
|
||||||
let attachment = attachments[currentIndex]
|
let attachment = attachments[currentIndex]
|
||||||
if attachment.url.pathExtension == "gif" {
|
if attachment.url.pathExtension == "gif" {
|
||||||
return ImageCache.attachments.get(attachment.url)
|
return ImageCache.attachments.getData(attachment.url)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,17 +44,11 @@ struct ComposeAvatarImageView: View {
|
||||||
|
|
||||||
private func loadImage() {
|
private func loadImage() {
|
||||||
guard let url = url else { return }
|
guard let url = url else { return }
|
||||||
request = ImageCache.avatars.get(url) { (data) in
|
request = ImageCache.avatars.get(url) { (_, image) in
|
||||||
if let data = data, let image = UIImage(data: data) {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.request = nil
|
self.request = nil
|
||||||
self.avatarImage = image
|
self.avatarImage = image
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.request = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,15 +45,14 @@ class EmojiCollectionViewCell: UICollectionViewCell {
|
||||||
func updateUI(emoji: Emoji) {
|
func updateUI(emoji: Emoji) {
|
||||||
currentEmojiShortcode = emoji.shortcode
|
currentEmojiShortcode = emoji.shortcode
|
||||||
|
|
||||||
imageRequest = ImageCache.emojis.get(emoji.url) { [weak self] (data) in
|
imageRequest = ImageCache.emojis.get(emoji.url) { [weak self] (_, image) in
|
||||||
if let data = data, let image = UIImage(data: data) {
|
guard let image = image else { return }
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self = self, self.currentEmojiShortcode == emoji.shortcode else { return }
|
guard let self = self, self.currentEmojiShortcode == emoji.shortcode else { return }
|
||||||
self.emojiImageView.image = image
|
self.emojiImageView.image = image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
|
|
|
@ -87,8 +87,8 @@ class FastSwitchingAccountView: UIView {
|
||||||
let controller = MastodonController.getForAccount(account)
|
let controller = MastodonController.getForAccount(account)
|
||||||
controller.getOwnAccount { [weak self] (result) in
|
controller.getOwnAccount { [weak self] (result) in
|
||||||
guard let self = self, case let .success(account) = result else { return }
|
guard let self = self, case let .success(account) = result else { return }
|
||||||
self.avatarRequest = ImageCache.avatars.get(account.avatar) { [weak avatarImageView] (data) in
|
self.avatarRequest = ImageCache.avatars.get(account.avatar) { [weak avatarImageView] (_, image) in
|
||||||
guard let avatarImageView = avatarImageView, let data = data, let image = UIImage(data: data) else { return }
|
guard let avatarImageView = avatarImageView, let image = image else { return }
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
avatarImageView.image = image
|
avatarImageView.image = image
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,19 +85,19 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
|
||||||
overrideUserInterfaceStyle = .dark
|
overrideUserInterfaceStyle = .dark
|
||||||
view.backgroundColor = .black
|
view.backgroundColor = .black
|
||||||
|
|
||||||
if let data = cache.get(url) {
|
if let (data, image) = cache.get(url) {
|
||||||
createLargeImage(data: data, url: url)
|
createLargeImage(data: data, image: image, url: url)
|
||||||
} else {
|
} else {
|
||||||
createPreview()
|
createPreview()
|
||||||
|
|
||||||
loadingVC = LoadingViewController()
|
loadingVC = LoadingViewController()
|
||||||
embedChild(loadingVC!)
|
embedChild(loadingVC!)
|
||||||
imageRequest = cache.get(url) { [weak self] (data) in
|
imageRequest = cache.get(url) { [weak self] (data, image) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.imageRequest = nil
|
self.imageRequest = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.loadingVC?.removeViewAndController()
|
self.loadingVC?.removeViewAndController()
|
||||||
self.createLargeImage(data: data!, url: self.url)
|
self.createLargeImage(data: data!, image: image!, url: self.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,20 +115,13 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createLargeImage(data: Data, url: URL) {
|
private func createLargeImage(data: Data, image: UIImage, url: URL) {
|
||||||
guard !loaded else { return }
|
guard !loaded else { return }
|
||||||
loaded = true
|
loaded = true
|
||||||
|
|
||||||
let image: UIImage?
|
if let transformedImage = ImageGrayscalifier.convertIfNecessary(url: url, image: image) {
|
||||||
if Preferences.shared.grayscaleImages {
|
|
||||||
image = ImageGrayscalifier.convert(url: url, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let image = image {
|
|
||||||
let gifData = url.pathExtension == "gif" ? data : nil
|
let gifData = url.pathExtension == "gif" ? data : nil
|
||||||
createLargeImage(image: image, gifData: gifData)
|
createLargeImage(image: transformedImage, gifData: gifData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,7 @@ struct LocalAccountAvatarView: View {
|
||||||
let controller = MastodonController.getForAccount(localAccountInfo)
|
let controller = MastodonController.getForAccount(localAccountInfo)
|
||||||
controller.getOwnAccount { (result) in
|
controller.getOwnAccount { (result) in
|
||||||
guard case let .success(account) = result else { return }
|
guard case let .success(account) = result else { return }
|
||||||
_ = ImageCache.avatars.get(account.avatar) { (data) in
|
_ = ImageCache.avatars.get(account.avatar) { (_, image) in
|
||||||
if let data = data, let image = UIImage(data: data) {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarImage = image
|
self.avatarImage = image
|
||||||
}
|
}
|
||||||
|
@ -47,7 +46,6 @@ struct LocalAccountAvatarView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//struct LocalAccountAvatarView_Previews: PreviewProvider {
|
//struct LocalAccountAvatarView_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
// static var previews: some View {
|
||||||
|
|
|
@ -43,17 +43,10 @@ class MyProfileViewController: ProfileViewController {
|
||||||
|
|
||||||
private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) {
|
private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) {
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
_ = ImageCache.avatars.get(avatarURL, completion: { [weak self] (data) in
|
_ = ImageCache.avatars.get(avatarURL, completion: { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data else { return }
|
guard let self = self,
|
||||||
|
let image = image,
|
||||||
let maybeGrayscale: UIImage?
|
let maybeGrayscale = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||||
if Preferences.shared.grayscaleImages {
|
|
||||||
maybeGrayscale = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
|
||||||
} else {
|
|
||||||
maybeGrayscale = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let image = maybeGrayscale else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +56,7 @@ class MyProfileViewController: ProfileViewController {
|
||||||
let tabBarImage = UIGraphicsImageRenderer(size: size).image { (_) in
|
let tabBarImage = UIGraphicsImageRenderer(size: size).image { (_) in
|
||||||
let radius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
let radius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
||||||
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
|
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
|
||||||
image.draw(in: rect)
|
maybeGrayscale.draw(in: rect)
|
||||||
}
|
}
|
||||||
let alwaysOriginalImage = tabBarImage.withRenderingMode(.alwaysOriginal)
|
let alwaysOriginalImage = tabBarImage.withRenderingMode(.alwaysOriginal)
|
||||||
self.tabBarItem.image = alwaysOriginalImage
|
self.tabBarItem.image = alwaysOriginalImage
|
||||||
|
|
|
@ -63,19 +63,16 @@ class AccountTableViewCell: UITableViewCell {
|
||||||
let accountID = self.accountID
|
let accountID = self.accountID
|
||||||
|
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.accountID == accountID else { return }
|
guard let self = self else { return }
|
||||||
self.avatarRequest = nil
|
self.avatarRequest = nil
|
||||||
|
|
||||||
let image: UIImage?
|
guard let image = image,
|
||||||
if self.isGrayscale {
|
self.accountID == accountID,
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarImageView.image = image
|
self.avatarImageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,11 +69,11 @@ class LargeAccountDetailView: UIView {
|
||||||
displayNameLabel.updateForAccountDisplayName(account: account)
|
displayNameLabel.updateForAccountDisplayName(account: account)
|
||||||
usernameLabel.text = "@\(account.acct)"
|
usernameLabel.text = "@\(account.acct)"
|
||||||
|
|
||||||
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data else { return }
|
guard let self = self, let image = image else { return }
|
||||||
self.avatarRequest = nil
|
self.avatarRequest = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarImageView.image = UIImage(data: data)
|
self.avatarImageView.image = image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,9 @@ struct AccountDisplayNameLabel<Account: AccountProtocol>: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
let request = ImageCache.emojis.get(emoji.url) { (data) in
|
let request = ImageCache.emojis.get(emoji.url) { (_, image) in
|
||||||
defer { group.leave() }
|
defer { group.leave() }
|
||||||
guard let data = data, let image = UIImage(data: data) else { return }
|
guard let image = image else { return }
|
||||||
|
|
||||||
let size = CGSize(width: fontSize, height: fontSize)
|
let size = CGSize(width: fontSize, height: fontSize)
|
||||||
let renderer = UIGraphicsImageRenderer(size: size)
|
let renderer = UIGraphicsImageRenderer(size: size)
|
||||||
|
|
|
@ -159,7 +159,7 @@ class AttachmentView: UIImageView, GIFAnimatable {
|
||||||
|
|
||||||
func loadImage() {
|
func loadImage() {
|
||||||
let attachmentURL = attachment.url
|
let attachmentURL = attachment.url
|
||||||
attachmentRequest = ImageCache.attachments.get(attachmentURL) { [weak self] (data) in
|
attachmentRequest = ImageCache.attachments.get(attachmentURL) { [weak self] (data, _) in
|
||||||
guard let self = self, let data = data else { return }
|
guard let self = self, let data = data else { return }
|
||||||
self.attachmentRequest = nil
|
self.attachmentRequest = nil
|
||||||
if self.attachment.url.pathExtension == "gif" {
|
if self.attachment.url.pathExtension == "gif" {
|
||||||
|
|
|
@ -43,20 +43,13 @@ extension BaseEmojiLabel {
|
||||||
foundEmojis = true
|
foundEmojis = true
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
let request = ImageCache.emojis.get(emoji.url) { (data) in
|
let request = ImageCache.emojis.get(emoji.url) { (_, image) in
|
||||||
defer { group.leave() }
|
defer { group.leave() }
|
||||||
guard let data = data else {
|
guard let image = image,
|
||||||
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: emoji.url, image: image) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let image: UIImage?
|
emojiImages[emoji.shortcode] = transformedImage
|
||||||
if Preferences.shared.grayscaleImages {
|
|
||||||
image = ImageGrayscalifier.convert(url: emoji.url, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
if let image = image {
|
|
||||||
emojiImages[emoji.shortcode] = image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let request = request {
|
if let request = request {
|
||||||
emojiRequests.append(request)
|
emojiRequests.append(request)
|
||||||
|
|
|
@ -63,20 +63,13 @@ class ContentTextView: LinkTextView {
|
||||||
|
|
||||||
for emoji in emojis {
|
for emoji in emojis {
|
||||||
group.enter()
|
group.enter()
|
||||||
_ = ImageCache.emojis.get(emoji.url) { (data) in
|
_ = ImageCache.emojis.get(emoji.url) { (_, image) in
|
||||||
defer { group.leave() }
|
defer { group.leave() }
|
||||||
guard let data = data else {
|
guard let image = image,
|
||||||
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: emoji.url, image: image) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let image: UIImage?
|
emojiImages[emoji.shortcode] = transformedImage
|
||||||
if Preferences.shared.grayscaleImages {
|
|
||||||
image = ImageGrayscalifier.convert(url: emoji.url, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
if let image = image {
|
|
||||||
emojiImages[emoji.shortcode] = image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,16 +33,12 @@ struct CustomEmojiImageView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadImage() {
|
private func loadImage() {
|
||||||
request = ImageCache.emojis.get(emoji.url) { (data) in
|
request = ImageCache.emojis.get(emoji.url) { (_, image) in
|
||||||
if let data = data, let image = UIImage(data: data) {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.request = nil
|
self.request = nil
|
||||||
|
if let image = image {
|
||||||
self.image = image
|
self.image = image
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.request = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ class InstanceTableViewCell: UITableViewCell {
|
||||||
private func updateThumbnail(url: URL) {
|
private func updateThumbnail(url: URL) {
|
||||||
thumbnailImageView.image = nil
|
thumbnailImageView.image = nil
|
||||||
thumbnailURL = url
|
thumbnailURL = url
|
||||||
thumbnailRequest = ImageCache.attachments.get(url) { [weak self] (data) in
|
thumbnailRequest = ImageCache.attachments.get(url) { [weak self] (_, image) in
|
||||||
guard let self = self, self.thumbnailURL == url, let data = data, let image = UIImage(data: data) else { return }
|
guard let self = self, self.thumbnailURL == url, let image = image else { return }
|
||||||
self.thumbnailRequest = nil
|
self.thumbnailRequest = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.thumbnailImageView.image = image
|
self.thumbnailImageView.image = image
|
||||||
|
|
|
@ -84,21 +84,20 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.group.id == group.id else { return }
|
guard let self = self else { return }
|
||||||
|
guard let image = image,
|
||||||
let image: UIImage?
|
self.group.id == group.id,
|
||||||
if self.isGrayscale {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let image = image {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarRequests.removeValue(forKey: account.id)
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
imageView.image = image
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
|
imageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actionAvatarStackView.addArrangedSubview(imageView)
|
actionAvatarStackView.addArrangedSubview(imageView)
|
||||||
|
@ -133,21 +132,20 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.group.id == groupID else { return }
|
guard let self = self else { return }
|
||||||
|
guard let image = image,
|
||||||
let image: UIImage?
|
self.group.id == groupID,
|
||||||
if self.isGrayscale {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let image = image {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarRequests.removeValue(forKey: account.id)
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
imageView.image = image
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
|
imageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,21 +65,17 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.group.id == group.id else { return }
|
guard let self = self,
|
||||||
|
let image = image,
|
||||||
let image: UIImage?
|
self.group.id == group.id,
|
||||||
if Preferences.shared.grayscaleImages {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
return
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let image = image {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarRequests.removeValue(forKey: account.id)
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
imageView.image = image
|
imageView.image = transformedImage
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avatarStackView.addArrangedSubview(imageView)
|
avatarStackView.addArrangedSubview(imageView)
|
||||||
|
@ -103,21 +99,20 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequests[account.id] = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.group.id == groupID else { return }
|
guard let self = self else { return }
|
||||||
|
guard let image = image,
|
||||||
let image: UIImage?
|
self.group.id == groupID,
|
||||||
if self.isGrayscale {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let image = image {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarRequests.removeValue(forKey: account.id)
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
imageView.image = image
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.avatarRequests.removeValue(forKey: account.id)
|
||||||
|
imageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,21 +68,18 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
|
||||||
actionLabel.setEmojis(account.emojis, identifier: account.id)
|
actionLabel.setEmojis(account.emojis, identifier: account.id)
|
||||||
}
|
}
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, self.account == account, let data = data else { return }
|
guard let self = self else { return }
|
||||||
self.avatarRequest = nil
|
self.avatarRequest = nil
|
||||||
|
|
||||||
let image: UIImage?
|
guard self.account == account,
|
||||||
if self.isGrayscale {
|
let image = image,
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
|
||||||
} else {
|
return
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let image = image {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarImageView.image = image
|
self.avatarImageView.image = transformedImage
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,35 +191,35 @@ class ProfileHeaderView: UIView {
|
||||||
|
|
||||||
let accountID = account.id
|
let accountID = account.id
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.accountID == accountID else { return }
|
guard let self = self, let image = image, self.accountID == accountID else { return }
|
||||||
self.avatarRequest = nil
|
self.avatarRequest = nil
|
||||||
|
|
||||||
let image: UIImage?
|
let transformedImage: UIImage?
|
||||||
if self.isGrayscale {
|
if self.isGrayscale {
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
transformedImage = ImageGrayscalifier.convert(url: avatarURL, cgImage: image.cgImage!)
|
||||||
} else {
|
} else {
|
||||||
image = UIImage(data: data)
|
transformedImage = image
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarImageView.image = image
|
self.avatarImageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let header = account.header {
|
if let header = account.header {
|
||||||
headerRequest = ImageCache.headers.get(header) { [weak self] (data) in
|
headerRequest = ImageCache.headers.get(header) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.accountID == accountID else { return }
|
guard let self = self, let image = image, self.accountID == accountID else { return }
|
||||||
self.headerRequest = nil
|
self.headerRequest = nil
|
||||||
|
|
||||||
let image: UIImage?
|
let transformedImage: UIImage?
|
||||||
if self.isGrayscale {
|
if self.isGrayscale {
|
||||||
image = ImageGrayscalifier.convert(url: header, data: data)
|
transformedImage = ImageGrayscalifier.convert(url: header, cgImage: image.cgImage!)
|
||||||
} else {
|
} else {
|
||||||
image = UIImage(data: data)
|
transformedImage = image
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.headerImageView.image = image
|
self.headerImageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,18 +260,14 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
let avatarURL = account.avatar
|
let avatarURL = account.avatar
|
||||||
let accountID = account.id
|
let accountID = account.id
|
||||||
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
|
||||||
guard let self = self, let data = data, self.accountID == accountID else { return }
|
guard let self = self,
|
||||||
|
let image = image,
|
||||||
let image: UIImage?
|
self.accountID == accountID,
|
||||||
if self.isGrayscale {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else { return }
|
||||||
image = ImageGrayscalifier.convert(url: avatarURL, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.avatarImageView.image = image
|
self.avatarImageView.image = transformedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,18 +141,13 @@ class StatusCardView: UIView {
|
||||||
if let imageURL = card.image {
|
if let imageURL = card.image {
|
||||||
placeholderImageView.isHidden = true
|
placeholderImageView.isHidden = true
|
||||||
|
|
||||||
imageRequest = ImageCache.attachments.get(imageURL, completion: { (data) in
|
imageRequest = ImageCache.attachments.get(imageURL, completion: { (_, image) in
|
||||||
guard let data = data else { return }
|
guard let image = image,
|
||||||
let image: UIImage?
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: imageURL, image: image) else {
|
||||||
if self.isGrayscale {
|
return
|
||||||
image = ImageGrayscalifier.convert(url: imageURL, data: data)
|
|
||||||
} else {
|
|
||||||
image = UIImage(data: data)
|
|
||||||
}
|
}
|
||||||
if let image = image {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.imageView.image = image
|
self.imageView.image = transformedImage
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if imageRequest != nil {
|
if imageRequest != nil {
|
||||||
|
|
Loading…
Reference in New Issue