diff --git a/.gitmodules b/.gitmodules index d3a62aadbf..b39a3e2f94 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "Cache"] path = Cache url = git@github.com:hyperoslo/Cache.git +[submodule "Gifu"] + path = Gifu + url = git://github.com/kaishin/Gifu.git diff --git a/Gifu b/Gifu new file mode 160000 index 0000000000..ed572f53ce --- /dev/null +++ b/Gifu @@ -0,0 +1 @@ +Subproject commit ed572f53ce58b8e23499abeb3a926033cbe480f7 diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 1808846544..6e19d704a6 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -115,6 +115,8 @@ D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; }; D6A5FAFB217B86CE003DB2D9 /* OnboardingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */; }; D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; }; + D6BC874521961F73006163F1 /* Gifu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; }; + D6BC874621961F73006163F1 /* Gifu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D6BED170212663DA00F02DA0 /* SwiftSoup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */; }; D6C693CA2161253F007D6A6D /* SilentActionPermissionsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693C92161253F007D6A6D /* SilentActionPermissionsTableViewController.swift */; }; @@ -194,6 +196,7 @@ 04496BD0216252E5001F1B23 /* TTTAttributedLabel.framework in Embed Frameworks */, D61099C12144B0CC00432DC2 /* Pachyderm.framework in Embed Frameworks */, D6BED170212663DA00F02DA0 /* SwiftSoup.framework in Embed Frameworks */, + D6BC874621961F73006163F1 /* Gifu.framework in Embed Frameworks */, 0461A3912163CBAE00C0A807 /* Cache.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -309,6 +312,7 @@ D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = ""; }; D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OnboardingViewController.xib; sourceTree = ""; }; D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = ""; }; + D6BC874421961F73006163F1 /* Gifu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Gifu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = ""; }; D6C693C92161253F007D6A6D /* SilentActionPermissionsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SilentActionPermissionsTableViewController.swift; sourceTree = ""; }; @@ -369,6 +373,7 @@ 04496BCF216252E5001F1B23 /* TTTAttributedLabel.framework in Frameworks */, D61099C02144B0CC00432DC2 /* Pachyderm.framework in Frameworks */, D65A37F321472F300087646E /* SwiftSoup.framework in Frameworks */, + D6BC874521961F73006163F1 /* Gifu.framework in Frameworks */, 0461A3902163CBAE00C0A807 /* Cache.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -742,6 +747,7 @@ D6D4DDC3212518A000E1C4BB = { isa = PBXGroup; children = ( + D6BC874421961F73006163F1 /* Gifu.framework */, 0461A38F2163CBAE00C0A807 /* Cache.framework */, D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */, D61099AC2144B0CC00432DC2 /* Pachyderm */, diff --git a/Tusker.xcworkspace/contents.xcworkspacedata b/Tusker.xcworkspace/contents.xcworkspacedata index 6f52af948b..8a254a57e5 100644 --- a/Tusker.xcworkspace/contents.xcworkspacedata +++ b/Tusker.xcworkspace/contents.xcworkspacedata @@ -10,4 +10,7 @@ + + diff --git a/Tusker/AppRouter.swift b/Tusker/AppRouter.swift index 2de742ccdf..1f89c8808a 100644 --- a/Tusker/AppRouter.swift +++ b/Tusker/AppRouter.swift @@ -91,6 +91,13 @@ class AppRouter { return vc } + func largeImage(gifData: Data, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, transitioningDelegate: UIViewControllerTransitioningDelegate?) -> LargeImageViewController { + let vc = LargeImageViewController(image: UIImage(data: gifData)!, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius, router: self) + vc.gifData = gifData + vc.transitioningDelegate = transitioningDelegate + return vc + } + func moreOptions(forStatus statusID: String) -> UIAlertController { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") } diff --git a/Tusker/Caching/ImageCache.swift b/Tusker/Caching/ImageCache.swift index f772a0d88d..b86c8a5242 100644 --- a/Tusker/Caching/ImageCache.swift +++ b/Tusker/Caching/ImageCache.swift @@ -15,45 +15,45 @@ class ImageCache { static let headers = ImageCache(name: "Headers", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24)) static let attachments = ImageCache(name: "Attachments", memoryExpiry: .seconds(60 * 2)) - let cache: Cache + let cache: Cache var requests = [URL: Request]() init(name: String, memoryExpiry expiry: Expiry) { - let storage = MemoryStorage(config: MemoryConfig(expiry: expiry)) + let storage = MemoryStorage(config: MemoryConfig(expiry: expiry)) self.cache = .memory(storage) } init(name: String, diskExpiry expiry: Expiry) { - let storage = try! DiskStorage(config: DiskConfig(name: name, expiry: expiry), transformer: TransformerFactory.forImage()) + let storage = try! DiskStorage(config: DiskConfig(name: name, expiry: expiry), transformer: TransformerFactory.forData()) self.cache = .disk(storage) } init(name: String, memoryExpiry: Expiry, diskExpiry: Expiry) { - let memory = MemoryStorage(config: MemoryConfig(expiry: memoryExpiry)) - let disk = try! DiskStorage(config: DiskConfig(name: name, expiry: diskExpiry), transformer: TransformerFactory.forImage()) + let memory = MemoryStorage(config: MemoryConfig(expiry: memoryExpiry)) + let disk = try! DiskStorage(config: DiskConfig(name: name, expiry: diskExpiry), transformer: TransformerFactory.forData()) self.cache = .hybrid(HybridStorage(memoryStorage: memory, diskStorage: disk)) } - func get(_ url: URL, completion: ((UIImage?) -> Void)?) { + func get(_ url: URL, completion: ((Data?) -> Void)?) { let key = url.absoluteString if (try? cache.existsObject(forKey: key)) ?? false, - let image = try? cache.object(forKey: key) { - completion?(image) + let data = try? cache.object(forKey: key) { + completion?(data) } else { if let completion = completion, let request = requests[url] { request.callbacks.append(completion) } else { let request = Request(url: url, completion: completion) requests[url] = request - request.run { (image) in - try? self.cache.setObject(image, forKey: key) + request.run { (data) in + try? self.cache.setObject(data, forKey: key) } } } } - func get(_ url: URL) -> UIImage? { + func get(_ url: URL) -> Data? { return try? cache.object(forKey: url.absoluteString) } @@ -64,9 +64,9 @@ class ImageCache { class Request { let url: URL var task: URLSessionDataTask? - var callbacks: [(UIImage?) -> Void] + var callbacks: [(Data?) -> Void] - init(url: URL, completion: ((UIImage?) -> Void)?) { + init(url: URL, completion: ((Data?) -> Void)?) { if let completion = completion { self.callbacks = [completion] } else { @@ -75,14 +75,14 @@ class ImageCache { self.url = url } - func run(cache: @escaping (UIImage) -> Void) { + func run(cache: @escaping (Data) -> Void) { task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in - guard error == nil, let data = data, let image = UIImage(data: data) else { + guard error == nil, let data = data else { self.complete(with: nil) return } - cache(image) - self.complete(with: image) + cache(data) + self.complete(with: data) }) task!.resume() } @@ -92,8 +92,8 @@ class ImageCache { complete(with: nil) } - func complete(with image: UIImage?) { - callbacks.forEach { $0(image) } + func complete(with data: Data?) { + callbacks.forEach { $0(data) } } } diff --git a/Tusker/Screens/Compose/ComposeViewController.swift b/Tusker/Screens/Compose/ComposeViewController.swift index 1fefe8a4b9..a11f9ae523 100644 --- a/Tusker/Screens/Compose/ComposeViewController.swift +++ b/Tusker/Screens/Compose/ComposeViewController.swift @@ -98,9 +98,10 @@ class ComposeViewController: UIViewController { inReplyToAvatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: inReplyToAvatarImageView) inReplyToAvatarImageView.layer.masksToBounds = true inReplyToAvatarImageView.image = nil - ImageCache.avatars.get(inReplyTo.account.avatar) { (image) in + ImageCache.avatars.get(inReplyTo.account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.inReplyToAvatarImageView.image = image + self.inReplyToAvatarImageView.image = UIImage(data: data) } } inReplyToLabel.text = "In reply to \(inReplyTo.account.realDisplayName)" diff --git a/Tusker/Screens/Large Image/LargeImageViewController.swift b/Tusker/Screens/Large Image/LargeImageViewController.swift index 8573c08049..fb2a4494e7 100644 --- a/Tusker/Screens/Large Image/LargeImageViewController.swift +++ b/Tusker/Screens/Large Image/LargeImageViewController.swift @@ -8,6 +8,7 @@ import UIKit import Photos +import Gifu class LargeImageViewController: UIViewController, UIScrollViewDelegate { @@ -18,7 +19,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate { var dismissInteractionController: LargeImageInteractionController? @IBOutlet weak var scrollView: UIScrollView! - @IBOutlet weak var imageView: UIImageView! + @IBOutlet weak var imageView: GIFImageView! @IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint! @IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint! @IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint! @@ -39,6 +40,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate { var initializedTopControlsConstrains = false var image: UIImage? + var gifData: Data? var imageDescription: String? var controlsVisible = true { @@ -70,6 +72,16 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate { super.init(nibName: "LargeImageViewController", bundle: nil) } +// init(gifData: Data?, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, router: AppRouter) { +// self.router = router +// self.gifData = gifData +// self.imageDescription = description +// self.originFrame = sourceFrame +// self.originCornerRadius = sourceCornerRadius +// +// super.init(nibName: "LargeImageViewController", bundle: nil) +// } + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -78,8 +90,12 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate { super.viewDidLoad() imageView.image = image + if let gifData = gifData { + imageView.animate(withGIFData: gifData) + } + scrollView.delegate = self - imageView.bounds = CGRect(origin: .zero, size: image!.size) + imageView.bounds = CGRect(origin: .zero, size: imageView.image!.size) if let imageDescription = imageDescription { descriptionLabel.text = imageDescription diff --git a/Tusker/Screens/Large Image/LargeImageViewController.xib b/Tusker/Screens/Large Image/LargeImageViewController.xib index 023aa14306..abd1d9d140 100644 --- a/Tusker/Screens/Large Image/LargeImageViewController.xib +++ b/Tusker/Screens/Large Image/LargeImageViewController.xib @@ -1,10 +1,10 @@ - + - + @@ -38,7 +38,7 @@ - + diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift index 83f90b731d..db708fb1c9 100644 --- a/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift +++ b/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift @@ -7,6 +7,7 @@ // import UIKit +import Gifu class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning { @@ -23,14 +24,17 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra let containerView = transitionContext.containerView let finalVCFrame = transitionContext.finalFrame(for: toVC) - let image = toVC.image! + let image = toVC.imageView.image! let ratio = image.size.width / image.size.height let width = finalVCFrame.width let height = width / ratio let finalFrame = CGRect(x: finalVCFrame.midX - width / 2, y: finalVCFrame.midY - height / 2, width: width, height: height) - let imageView = UIImageView(frame: originFrame) - imageView.image = toVC.image! + let imageView = GIFImageView(frame: originFrame) + imageView.image = toVC.imageView.image! + if let gifData = toVC.gifData { + imageView.animate(withGIFData: gifData) + } imageView.contentMode = .scaleAspectFill imageView.layer.cornerRadius = toVC.originCornerRadius! imageView.layer.masksToBounds = true diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift index e188740ca6..ee34bc8b21 100644 --- a/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift +++ b/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift @@ -7,6 +7,7 @@ // import UIKit +import Gifu class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning { @@ -36,8 +37,11 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra let height = width / ratio let originalFrame = CGRect(x: originalVCFrame.midX - width / 2, y: originalVCFrame.midY - height / 2, width: width, height: height) - let imageView = UIImageView(frame: originalFrame) + let imageView = GIFImageView(frame: originalFrame) imageView.image = fromVC.image! + if let gifData = fromVC.gifData { + imageView.animate(withGIFData: gifData) + } imageView.contentMode = .scaleAspectFill imageView.layer.cornerRadius = 0 imageView.layer.masksToBounds = true diff --git a/Tusker/TuskerNavigationDelegate.swift b/Tusker/TuskerNavigationDelegate.swift index 47d3e0cfc4..9365d1f6be 100644 --- a/Tusker/TuskerNavigationDelegate.swift +++ b/Tusker/TuskerNavigationDelegate.swift @@ -30,8 +30,12 @@ protocol TuskerNavigationDelegate { func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController + func largeImage(gifData: Data, description: String?, sourceView: UIView) -> LargeImageViewController + func showLargeImage(_ image: UIImage, description: String?, animatingFrom sourceView: UIView) + func showLargeImage(gifData: Data, description: String?, animatingFrom sourceView: UIView) + func showMoreOptions(forStatus statusID: String) func showMoreOptions(forURL url: URL) @@ -95,10 +99,28 @@ extension TuskerNavigationDelegate where Self: UIViewController { return router.largeImage(image, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius, transitioningDelegate: self) } + func largeImage(gifData: Data, description: String?, sourceView: UIView) -> LargeImageViewController { + var sourceFrame = sourceView.convert(sourceView.bounds, to: view) + if let scrollView = view as? UIScrollView { + let scale = scrollView.zoomScale + let width = sourceFrame.width * scale + let height = sourceFrame.height * scale + let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX + let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY + sourceFrame = CGRect(x: x, y: y, width: width, height: height) + } + let sourceCornerRadius = sourceView.layer.cornerRadius + return router.largeImage(gifData: gifData, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius, transitioningDelegate: self) + } + func showLargeImage(_ image: UIImage, description: String?, animatingFrom sourceView: UIView) { router.present(largeImage(image, description: description, sourceView: sourceView), animated: true) } + func showLargeImage(gifData: Data, description: String?, animatingFrom sourceView: UIView) { + router.present(largeImage(gifData: gifData, description: description, sourceView: sourceView), animated: true) + } + func showMoreOptions(forStatus statusID: String) { router.present(router.moreOptions(forStatus: statusID), animated: true) } diff --git a/Tusker/Views/AttachmentView.swift b/Tusker/Views/AttachmentView.swift index d85aa832f9..24c38cbae6 100644 --- a/Tusker/Views/AttachmentView.swift +++ b/Tusker/Views/AttachmentView.swift @@ -8,16 +8,20 @@ import UIKit import Pachyderm +import Gifu protocol AttachmentViewDelegate { func showLargeAttachment(for attachmentView: AttachmentView) } -class AttachmentView: UIImageView { +class AttachmentView: UIImageView, GIFAnimatable { var delegate: AttachmentViewDelegate? var attachment: Attachment! + var gifData: Data? + + public lazy var animator: Animator? = Animator(withDelegate: self) required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -43,13 +47,23 @@ class AttachmentView: UIImageView { } func loadImage() { - ImageCache.attachments.get(attachment.url) { (image) in + ImageCache.attachments.get(attachment.url) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.image = image + if self.attachment.url.pathExtension == "gif" { + self.animate(withGIFData: data) + self.gifData = data + } else { + self.image = UIImage(data: data) + } } } } + override func display(_ layer: CALayer) { + updateImageIfNeeded() + } + @objc func imagePressed() { if image != nil { delegate?.showLargeAttachment(for: self) diff --git a/Tusker/Views/Notifications/ActionNotificationTableViewCell.swift b/Tusker/Views/Notifications/ActionNotificationTableViewCell.swift index 5cf7dcdb44..67591c1beb 100644 --- a/Tusker/Views/Notifications/ActionNotificationTableViewCell.swift +++ b/Tusker/Views/Notifications/ActionNotificationTableViewCell.swift @@ -79,17 +79,19 @@ class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive { usernameLabel.text = "@\(status.account.acct)" opAvatarImageView.image = nil opAvatarURL = status.account.avatar - ImageCache.avatars.get(status.account.avatar) { (image) in + ImageCache.avatars.get(status.account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.opAvatarImageView.image = image + self.opAvatarImageView.image = UIImage(data: data) self.opAvatarURL = nil } } actionAvatarImageView.image = nil actionAvatarURL = notification.account.avatar - ImageCache.avatars.get(notification.account.avatar) { (image) in + ImageCache.avatars.get(notification.account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.actionAvatarImageView.image = image + self.actionAvatarImageView.image = UIImage(data: data) self.actionAvatarURL = nil } } diff --git a/Tusker/Views/Notifications/FollowNotificationTableViewCell.swift b/Tusker/Views/Notifications/FollowNotificationTableViewCell.swift index fa08623f31..7ca3e29091 100644 --- a/Tusker/Views/Notifications/FollowNotificationTableViewCell.swift +++ b/Tusker/Views/Notifications/FollowNotificationTableViewCell.swift @@ -51,9 +51,10 @@ class FollowNotificationTableViewCell: UITableViewCell, PreferencesAdaptive { usernameLabel.text = "@\(account.acct)" avatarImageView.image = nil avatarURL = account.avatar - ImageCache.avatars.get(account.avatar) { (image) in + ImageCache.avatars.get(account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.avatarImageView.image = image + self.avatarImageView.image = UIImage(data: data) self.avatarURL = nil } } diff --git a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift index 47fc7f9e4c..b6deb8f2fc 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift +++ b/Tusker/Views/Profile Header/ProfileHeaderTableViewCell.swift @@ -61,15 +61,17 @@ class ProfileHeaderTableViewCell: UITableViewCell, PreferencesAdaptive { avatarImageView.image = nil avatarURL = account.avatar - ImageCache.avatars.get(account.avatar) { (image) in + ImageCache.avatars.get(account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.avatarImageView.image = image + self.avatarImageView.image = UIImage(data: data) self.avatarURL = nil } } - ImageCache.headers.get(account.header) { (image) in + ImageCache.headers.get(account.header) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.headerImageView.image = image + self.headerImageView.image = UIImage(data: data) self.headerURL = nil } } diff --git a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift index d107ccfdf1..cc056e014d 100644 --- a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift @@ -80,9 +80,10 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive usernameLabel.text = "@\(account.acct)" avatarImageView.image = nil avatarURL = account.avatar - ImageCache.avatars.get(account.avatar) { (image) in + ImageCache.avatars.get(account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.avatarImageView.image = image + self.avatarImageView.image = UIImage(data: data) self.avatarURL = nil } } @@ -221,7 +222,11 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive extension ConversationMainStatusTableViewCell: AttachmentViewDelegate { func showLargeAttachment(for attachmentView: AttachmentView) { - delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView) + if let gifData = attachmentView.gifData { + delegate?.showLargeImage(gifData: gifData, description: attachmentView.attachment.description, animatingFrom: attachmentView) + } else { + delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView) + } } } diff --git a/Tusker/Views/Status/StatusTableViewCell.swift b/Tusker/Views/Status/StatusTableViewCell.swift index 6b74eaada1..7e5a17d623 100644 --- a/Tusker/Views/Status/StatusTableViewCell.swift +++ b/Tusker/Views/Status/StatusTableViewCell.swift @@ -97,9 +97,10 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive { usernameLabel.text = "@\(account.acct)" avatarImageView.image = nil avatarURL = account.avatar - ImageCache.avatars.get(account.avatar) { (image) in + ImageCache.avatars.get(account.avatar) { (data) in + guard let data = data else { return } DispatchQueue.main.async { - self.avatarImageView.image = image + self.avatarImageView.image = UIImage(data: data) self.avatarURL = nil } } @@ -352,7 +353,11 @@ extension StatusTableViewCell: TableViewSwipeActionProvider { extension StatusTableViewCell: AttachmentViewDelegate { func showLargeAttachment(for attachmentView: AttachmentView) { - delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView) + if let gifData = attachmentView.gifData { + delegate?.showLargeImage(gifData: gifData, description: attachmentView.attachment.description, animatingFrom: attachmentView) + } else { + delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView) + } } }