From e7a920b8d06f1aaf0f86826e862bb5fced909356 Mon Sep 17 00:00:00 2001 From: Glenn Posadas Date: Thu, 15 Apr 2021 03:25:03 +0800 Subject: [PATCH] fix image loading --- .../Screens/Search/MovieTableViewCell.swift | 31 ++++++------------- .../ImageLoader/ImageLoaderService.swift | 20 ++++++------ .../ImageLoader/ImageLoaderServiceType.swift | 2 +- TMDB/Sources/UseCases/MoviesUseCase.swift | 25 ++++++++------- 4 files changed, 35 insertions(+), 43 deletions(-) diff --git a/TMDB/Sources/Screens/Search/MovieTableViewCell.swift b/TMDB/Sources/Screens/Search/MovieTableViewCell.swift index ab160e4..82de7e6 100644 --- a/TMDB/Sources/Screens/Search/MovieTableViewCell.swift +++ b/TMDB/Sources/Screens/Search/MovieTableViewCell.swift @@ -17,32 +17,19 @@ class MovieTableViewCell: UITableViewCell, NibProvidable, ReusableView { @IBOutlet private var poster: UIImageView! private var cancellable: AnyCancellable? - override func prepareForReuse() { - super.prepareForReuse() - cancelImageLoading() - } - func bind(to viewModel: MovieViewModel) { cancelImageLoading() title.text = viewModel.title subtitle.text = viewModel.subtitle rating.text = viewModel.rating - cancellable = viewModel.poster.sink { [unowned self] image in self.showImage(image: image) } + + cancellable = viewModel.poster + .receive(on: DispatchQueue.main) + .assign(to: \.poster.image, on: self) } - - private func showImage(image: UIImage?) { - cancelImageLoading() - UIView.transition(with: self.poster, - duration: 0.3, - options: [.curveEaseOut, .transitionCrossDissolve], - animations: { - self.poster.image = image - }) - } - - private func cancelImageLoading() { - poster.image = nil - cancellable?.cancel() - } - + + private func cancelImageLoading() { + poster.image = nil + cancellable?.cancel() + } } diff --git a/TMDB/Sources/Services/ImageLoader/ImageLoaderService.swift b/TMDB/Sources/Services/ImageLoader/ImageLoaderService.swift index 8da9a30..30c32b8 100644 --- a/TMDB/Sources/Services/ImageLoader/ImageLoaderService.swift +++ b/TMDB/Sources/Services/ImageLoader/ImageLoaderService.swift @@ -11,21 +11,23 @@ import UIKit.UIImage import Combine final class ImageLoaderService: ImageLoaderServiceType { - + private let cache: ImageCacheType = ImageCache() - - func loadImage(from url: URL) -> AnyPublisher { + + func loadImage(from url: URL, placeholder: UIImage? = nil) -> AnyPublisher { if let image = cache.image(for: url) { return .just(image) } return URLSession.shared.dataTaskPublisher(for: url) - .map { (data, response) -> UIImage? in return UIImage(data: data) } - .catch { error in return Just(nil) } - .handleEvents(receiveOutput: {[unowned self] image in - guard let image = image else { return } + .tryMap { data, response -> UIImage in + guard let image = UIImage(data: data) else { + throw NSError(domain: "", code: 1, userInfo: nil) + } + self.cache.insertImage(image, for: url) - }) - .print("Image loading \(url):") + return image + } + .replaceError(with: placeholder) .eraseToAnyPublisher() } } diff --git a/TMDB/Sources/Services/ImageLoader/ImageLoaderServiceType.swift b/TMDB/Sources/Services/ImageLoader/ImageLoaderServiceType.swift index 51c1e02..07e7bf8 100644 --- a/TMDB/Sources/Services/ImageLoader/ImageLoaderServiceType.swift +++ b/TMDB/Sources/Services/ImageLoader/ImageLoaderServiceType.swift @@ -11,5 +11,5 @@ import UIKit.UIImage import Combine protocol ImageLoaderServiceType: AnyObject, AutoMockable { - func loadImage(from url: URL) -> AnyPublisher + func loadImage(from url: URL, placeholder: UIImage?) -> AnyPublisher } diff --git a/TMDB/Sources/UseCases/MoviesUseCase.swift b/TMDB/Sources/UseCases/MoviesUseCase.swift index 3cfc2c5..c49773e 100644 --- a/TMDB/Sources/UseCases/MoviesUseCase.swift +++ b/TMDB/Sources/UseCases/MoviesUseCase.swift @@ -53,16 +53,19 @@ final class MoviesUseCase: MoviesUseCaseType { } func loadImage(for movie: Movie, size: ImageSize) -> AnyPublisher { - return Deferred { return Just(movie.poster) } - .flatMap({[unowned self] poster -> AnyPublisher in - guard let poster = movie.poster else { return .just(nil) } - let url = size.url.appendingPathComponent(poster) - return self.imageLoaderService.loadImage(from: url) - }) - .subscribe(on: Scheduler.backgroundWorkScheduler) - .receive(on: Scheduler.mainScheduler) - .share() - .eraseToAnyPublisher() + guard let poster = movie.poster else { return .just(nil) } + let url = size.url.appendingPathComponent(poster) + return imageLoaderService.loadImage(from: url, placeholder: imageFrom(color: .gray)) + } + + func imageFrom(color: UIColor) -> UIImage { + let rect = CGRect(x: 0, y: 0, width: 1, height: 1) + UIGraphicsBeginImageContext(rect.size) + let context = UIGraphicsGetCurrentContext() + context!.setFillColor(color.cgColor) + context!.fill(rect) + let img = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return img! } - }