Загрузка изображения в TableViewCell


#1

Здравствуйте
В моем приложении изображения в TableViewCell подгружаются из Firebase Storage если его нету, а если он есть то берется из FileManager.
Это работает так:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! StartTVCell
    let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("image").appendingPathComponent(filterAll[indexPath.row].image + ".jpg")
    if FileManager.default.fileExists(atPath: fileURL.path) {
        print("Yes image in directory")
        cell.imageV.image = loadImage(filterAll[indexPath.row].image)
    } else {
        print("No image in directory")
        let queue = DispatchQueue.global(qos: .userInteractive)
        queue.async {
            self.writeLocal(filterAll[indexPath.row].image)
            DispatchQueue.main.async {
                cell.imageV.image = self.loadImage(filterAll[indexPath.row].image)
            }
        }
    }
    return cell
}

Это функция подгрузки изображения из FileManager

func loadImage(_ image1: String) -> UIImage? {
    let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("image").appendingPathComponent(image1 + ".jpg")
    if FileManager.default.fileExists(atPath: fileURL.path) {
        do {
            return UIImage(data: try Data.init(contentsOf: fileURL))
        } catch {
            print("error getting image")
        }
    } else {
        print("No image in directory")
    }
    return UIImage(named: "")
}

Это функция загрузки изображения из Firebase Storage в FileManager.

func writeLocal(_ image1: String) {
    let localURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("image").appendingPathComponent(image1 + ".jpg")
    Storage.storage().reference().child("image").child(image1 + ".jpg").write(toFile: localURL) { url, error in
        if let error = error {
            print("write image no", error)
        } else {
            //self.tableView.reloadData()
            print("write image save yes")
        }
    }
}

Код полностью рабочий, но есть небольшая проблема…
Незагруженные изображения из Firebase Storage загружаются какое-то время и ячейки остаётся пустыми, изображения появляются только после скроллинга (то есть обновлении ячеек).

Как сделать чтобы изображения появлялись после загрузки сами?

Можно поставить self.tableView.reloadData() в функции func writeLocal(_ image1: String), но тогда при скроллинге все очень сильно дергается!


#2

Перенесите всю загрузку картинок в класс ячейки и делайте это там.


#3

Пробовал, все тоже самое


#4

Понял, не сразу дошло.

func writeLocal(_ image1: String, completion: (() -> ())?) {
    let localURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("image").appendingPathComponent(image1 + ".jpg")
    Storage.storage().reference().child("image").child(image1 + ".jpg").write(toFile: localURL) { url, error in
        if let error = error {
            print("write image no", error)
        } else {
            //self.tableView.reloadData()
            print("write image save yes")
            completion()?
        }
    }
}

и тогда сохранение и после этого загрузка будет таким

self.writeLocal(filterAll[indexPath.row].image) {
    cell.imageV.image = self.loadImage(filterAll[indexPath.row].image)
}

#5

Работает! Спасибо Вам большое! :+1:


#6

Если у вас в процессе меняются картинки в удалённой БД (firebase), то удобнее использовать NSCache
Создаёте расширение к имеджВью

extension UIImageView {
    func loadImageFromFBStorageWithCache(defaultImage: UIImage, imageStorageRef: StorageReference) {
        
        if let cachedImage = imageCacheFB.object(forKey: imageStorageRef) as? UIImage {
            self.image = cachedImage
            return
        }
        
        self.image = defaultImage
        
        DispatchQueue.main.async {
            imageStorageRef.getData(maxSize: 1*1024*1024) { (data, error) in
                if error != nil {
                    return
                }
                
                if let downloadImage = UIImage(data: data!) {
                    imageCacheFB.setObject(downloadImage, forKey: imageStorageRef)
                    self.image = downloadImage
                }
                
            }
        }
        
    }
    func removeCacheImage(imageStorageRef: StorageReference) {
        imageCacheFB.removeObject(forKey: imageStorageRef)
    }
}

Использование:

imageView.loadImageFromFBStorageWithCache(defaultImage: "дефолтная картинка, если нет в БД", imageStorageRef: "ссылка на картинку в БД")

removeCacheImage(imageStorageRef: StorageReference) - удаление кэша, если надо обновить кэшированную картинку в процессе работы приложения.


#7

Спасибо, так тоже по пробую)