Browse Source

Merge pull request #834 from deltachat/async_gallery_follow_up

Async gallery follow up
cyBerta 5 years ago
parent
commit
e8b87a58e1

+ 1 - 1
deltachat-ios/AppDelegate.swift

@@ -26,7 +26,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         DBDebugToolkit.setup(with: []) // empty array will override default device shake trigger
         DBDebugToolkit.setupCrashReporting()
-
+        
         let console = ConsoleDestination()
         logger.addDestination(console)
         dcContext.logger = DcLogger()

+ 100 - 3
deltachat-ios/Controller/GalleryViewController.swift

@@ -1,10 +1,12 @@
 import UIKit
 import DcCore
+import SDWebImage
 
 class GalleryViewController: UIViewController {
 
     // MARK: - data
     private let mediaMessageIds: [Int]
+    private var items: [Int: GalleryItem] = [:]
 
     // MARK: - subview specs
     private let gridDefaultSpacing: CGFloat = 5
@@ -26,6 +28,8 @@ class GalleryViewController: UIViewController {
         collection.backgroundColor = DcColors.defaultBackgroundColor
         collection.delaysContentTouches = false
         collection.alwaysBounceVertical = true
+        collection.isPrefetchingEnabled = true
+        collection.prefetchDataSource = self
         return collection
     }()
 
@@ -102,8 +106,17 @@ class GalleryViewController: UIViewController {
     }
 }
 
+extension GalleryViewController: UICollectionViewDataSourcePrefetching {
+    func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
+         indexPaths.forEach { if items[$0.row] == nil {
+            let item = GalleryItem(msgId: mediaMessageIds[$0.row])
+            items[$0.row] = item
+        }}
+    }
+}
+
 // MARK: - UICollectionViewDataSource, UICollectionViewDelegate
-extension GalleryViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
+extension GalleryViewController: UICollectionViewDataSource, UICollectionViewDelegate {
 
     func numberOfSections(in collectionView: UICollectionView) -> Int {
         return 1
@@ -119,9 +132,17 @@ extension GalleryViewController: UICollectionViewDataSource, UICollectionViewDel
             for: indexPath) as? GalleryCell else {
             return UICollectionViewCell()
         }
+
         let msgId = mediaMessageIds[indexPath.row]
-        let msg = DcMsg(id: msgId)
-        galleryCell.update(msg: msg)
+        var item: GalleryItem
+        if let galleryItem = items[indexPath.row] {
+            item = galleryItem
+        } else {
+            let galleryItem = GalleryItem(msgId: msgId)
+            items[indexPath.row] = galleryItem
+            item = galleryItem
+        }
+        galleryCell.update(item: item)
         return galleryCell
     }
 
@@ -197,3 +218,79 @@ extension GalleryViewController {
         present(previewController, animated: true, completion: nil)
     }
 }
+
+class GalleryItem {
+
+    var onImageLoaded: ((UIImage?) -> Void)?
+
+    var msg: DcMsg
+
+    var fileUrl: URL? {
+        return msg.fileURL
+    }
+
+    var thumbnailImage: UIImage? {
+        willSet {
+           onImageLoaded?(newValue)
+        }
+    }
+
+    var showPlayButton: Bool {
+        switch msg.viewtype {
+        case .video:
+            return true
+        default:
+            return false
+        }
+    }
+
+    init(msgId: Int) {
+        self.msg = DcMsg(id: msgId)
+
+        if let key = msg.fileURL?.absoluteString, let image = ThumbnailCache.shared.restoreImage(key: key) {
+            self.thumbnailImage = image
+        } else {
+            loadThumbnail()
+        }
+    }
+
+    private func loadThumbnail() {
+        guard let viewtype = msg.viewtype, let url = msg.fileURL else {
+            return
+        }
+        switch viewtype {
+        case .image:
+            thumbnailImage = msg.image
+        case .video:
+            loadVideoThumbnail(from: url)
+        case .gif:
+            loadGifThumbnail(from: url)
+        default:
+           safe_fatalError("unsupported viewtype - viewtype \(viewtype) not supported.")
+        }
+    }
+
+    private func loadGifThumbnail(from url: URL) {
+        DispatchQueue.global(qos: .userInteractive).async {
+            guard let imageData = try? Data(contentsOf: url) else {
+                return
+            }
+            let thumbnailImage = SDAnimatedImage(data: imageData)
+            DispatchQueue.main.async { [weak self] in
+                self?.thumbnailImage = thumbnailImage
+            }
+        }
+    }
+
+    private func loadVideoThumbnail(from url: URL) {
+        DispatchQueue.global(qos: .userInteractive).async {
+            let thumbnailImage = DcUtils.generateThumbnailFromVideo(url: url)
+            DispatchQueue.main.async { [weak self] in
+                self?.thumbnailImage = thumbnailImage
+                if let image = thumbnailImage {
+                    ThumbnailCache.shared.storeImage(image: image, key: url.absoluteString)
+                }
+            }
+        }
+    }
+}

+ 16 - 28
deltachat-ios/View/Cell/GalleryCell.swift

@@ -5,8 +5,10 @@ import SDWebImage
 class GalleryCell: UICollectionViewCell {
     static let reuseIdentifier = "gallery_cell"
 
-    var imageView: UIImageView = {
-        let view = UIImageView()
+    weak var item: GalleryItem?
+
+    var imageView: SDAnimatedImageView = {
+        let view = SDAnimatedImageView()
         view.contentMode = .scaleAspectFill
         view.clipsToBounds = true
         view.backgroundColor = DcColors.defaultBackgroundColor
@@ -19,7 +21,6 @@ class GalleryCell: UICollectionViewCell {
         return playButtonView
     }()
 
-
     override init(frame: CGRect) {
         super.init(frame: frame)
         setupSubviews()
@@ -29,6 +30,12 @@ class GalleryCell: UICollectionViewCell {
         fatalError("init(coder:) has not been implemented")
     }
 
+    override func prepareForReuse() {
+        super.prepareForReuse()
+        item?.onImageLoaded = nil
+        item = nil
+    }
+
     private func setupSubviews() {
         contentView.addSubview(imageView)
         imageView.translatesAutoresizingMaskIntoConstraints = false
@@ -43,32 +50,13 @@ class GalleryCell: UICollectionViewCell {
         playButtonView.constraint(equalTo: CGSize(width: 50, height: 50))
     }
 
-    func update(msg: DcMsg) {
-        guard let viewtype = msg.viewtype, let fileUrl = msg.fileURL else {
-            return
-        }
-        switch viewtype {
-        case .image:
-            imageView.image = msg.image
-            playButtonView.isHidden = true
-        case .video:
-            let key = fileUrl.absoluteString
-            if let image = ThumbnailCache.shared.restoreImage(key: key) {
-                imageView.image = image
-            } else {
-                imageView.loadVideoThumbnail(from: fileUrl, placeholderImage: nil) { thumbnail in
-                    if let image = thumbnail {
-                        ThumbnailCache.shared.storeImage(image: image, key: key)
-                    }
-                }
-            }
-            playButtonView.isHidden = false
-        case .gif:
-            imageView.sd_setImage(with: fileUrl, placeholderImage: nil)
-            playButtonView.isHidden = true
-        default:
-            safe_fatalError("unsupported viewtype - viewtype \(viewtype) not supported.")
+    func update(item: GalleryItem) {
+        self.item = item
+        item.onImageLoaded = { [weak self] image in
+            self?.imageView.image = image
         }
+        playButtonView.isHidden = !item.showPlayButton
+        imageView.image = item.thumbnailImage
     }
 
     override var isSelected: Bool {