Skip to content

Commit 81a65c0

Browse files
authored
image duplication fix (#2)
image duplication fixes when fast scrolling
1 parent ec15a2e commit 81a65c0

File tree

7 files changed

+72
-44
lines changed

7 files changed

+72
-44
lines changed

FlickrSearchApp.xcodeproj/project.pbxproj

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@
523523
isa = PBXProject;
524524
attributes = {
525525
LastSwiftUpdateCheck = 1000;
526-
LastUpgradeCheck = 1000;
526+
LastUpgradeCheck = 1200;
527527
ORGANIZATIONNAME = "Suhit Patil";
528528
TargetAttributes = {
529529
56290FA8215E62F300FB34C6 = {
@@ -709,6 +709,7 @@
709709
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
710710
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
711711
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
712+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
712713
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
713714
CLANG_WARN_STRICT_PROTOTYPES = YES;
714715
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -770,6 +771,7 @@
770771
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
771772
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
772773
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
774+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
773775
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
774776
CLANG_WARN_STRICT_PROTOTYPES = YES;
775777
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -806,7 +808,7 @@
806808
CODE_SIGN_STYLE = Automatic;
807809
DEVELOPMENT_TEAM = MHFE84M79V;
808810
INFOPLIST_FILE = "$(SRCROOT)/FlickrSearchAppUITests/Info.plist";
809-
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
811+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
810812
LD_RUNPATH_SEARCH_PATHS = (
811813
"$(inherited)",
812814
"@executable_path/Frameworks",
@@ -825,7 +827,7 @@
825827
CODE_SIGN_STYLE = Automatic;
826828
DEVELOPMENT_TEAM = MHFE84M79V;
827829
INFOPLIST_FILE = "$(SRCROOT)/FlickrSearchAppUITests/Info.plist";
828-
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
830+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
829831
LD_RUNPATH_SEARCH_PATHS = (
830832
"$(inherited)",
831833
"@executable_path/Frameworks",

FlickrSearchApp/Constants/Constants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ enum Strings {
2525
enum Constants {
2626
static let screenWidth: CGFloat = UIScreen.main.bounds.width
2727
static let defaultSpacing: CGFloat = 1
28-
static let numberOfColumns: CGFloat = 3
28+
static let numberOfColumns: CGFloat = 2
2929
static let defaultPageNum: Int = 0
3030
static let defaultTotalCount: Int = 0
3131
static let defaultPageSize: Int = 20

FlickrSearchApp/Extensions/UIImageView+Extensions.swift

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,75 @@
88

99
import UIKit
1010

11+
struct AssociatedKeys {
12+
static var imageUrlKey: UInt64 = 0
13+
}
1114

1215
extension UIImageView {
1316

14-
/*!
15-
* @description: Load Image into imageView from the given imageUrl using ImageDownloader
16-
* class
17-
* @parameters: takes imageURL, placeholderImage and indexPath as params
18-
*/
19-
func loadImage(with imageURL: URL, placeholder: UIImage? = UIImage(color: .black), size: CGSize, indexPath: IndexPath?) {
20-
self.image = placeholder
17+
private(set) var downloadImageURL: URL? {
18+
get {
19+
guard let value = objc_getAssociatedObject(self, &AssociatedKeys.imageUrlKey) as? URL else {
20+
return nil
21+
}
22+
return value
23+
}
24+
set(newValue) {
25+
objc_setAssociatedObject(self, &AssociatedKeys.imageUrlKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
26+
}
27+
}
28+
29+
30+
/// load image from remote url
31+
/// - Parameters:
32+
/// - imageURL: is the `URL` which points to Https url
33+
/// - placeholder: `UIImage` placeholder
34+
/// - size: image container size
35+
func loadImage(
36+
with imageURL: URL,
37+
placeholder: UIImage? = UIImage(color: .black),
38+
size: CGSize
39+
) {
40+
downloadImageURL = imageURL
41+
image = placeholder
2142
ImageDownloader.shared.downloadImage(
2243
withURL: imageURL,
2344
size: size,
24-
indexPath: indexPath,
25-
completion: { [weak self] (image: UIImage?, resultIndexPath: IndexPath?, url: URL, error: Error?) in
26-
if let self = self, let kIndexPath = resultIndexPath, indexPath == kIndexPath, imageURL.absoluteString == url.absoluteString {
27-
DispatchQueue.main.async {
28-
if let downloadedImage = image {
29-
self.fadeTransition(with: downloadedImage)
45+
completion: { [weak self] (image, isCached, url, error) in
46+
guard let self = self, let downloadURL = self.downloadImageURL, imageURL == downloadURL else {
47+
return
48+
}
49+
DispatchQueue.main.async {
50+
if let downloadedImage = image, let isCached = isCached {
51+
if isCached {
52+
self.image = downloadedImage
53+
} else {
54+
UIView.transition(with: self, duration: 0.5, options: .transitionCrossDissolve, animations: {
55+
self.image = downloadedImage
56+
}, completion: nil)
3057
}
58+
} else {
59+
self.image = placeholder
3160
}
3261
}
33-
})
62+
}
63+
)
3464
}
3565

3666
//MARK: Fade transition Animation
37-
func fadeTransition(with image: UIImage?, duration: TimeInterval = 0.5, completion: ((Bool) -> Void)? = nil) {
38-
UIView.transition(with: self, duration: duration, options: .transitionCrossDissolve, animations: {
39-
self.image = image
40-
}, completion: completion)
67+
func fadeTransition(
68+
with image: UIImage?,
69+
duration: TimeInterval = 0.5,
70+
completion: ((Bool) -> Void)? = nil
71+
) {
72+
UIView.transition(
73+
with: self,
74+
duration: duration,
75+
options: .transitionCrossDissolve,
76+
animations: {
77+
self.image = image
78+
},
79+
completion: completion
80+
)
4181
}
4282
}

FlickrSearchApp/ImageDownloader/ImageDownloader.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import Foundation
1010
import UIKit
1111

12-
typealias ImageDownloadCompletionHander = ((UIImage?, IndexPath?, URL, Error?) -> Void)
12+
typealias ImageDownloadCompletionHander = ((UIImage?, Bool?, URL, Error?) -> Void)
1313

1414
final class ImageDownloader {
1515

@@ -28,10 +28,9 @@ final class ImageDownloader {
2828

2929
private init() {}
3030

31-
func downloadImage(withURL imageURL: URL, size: CGSize, scale: CGFloat = UIScreen.main.scale, indexPath: IndexPath?, completion: @escaping ImageDownloadCompletionHander) {
32-
31+
func downloadImage(withURL imageURL: URL, size: CGSize, scale: CGFloat = UIScreen.main.scale, completion: @escaping ImageDownloadCompletionHander) {
3332
if let cachedImage = imageCache.object(forKey: imageURL.absoluteString as NSString) {
34-
completion(cachedImage, indexPath, imageURL, nil)
33+
completion(cachedImage, true, imageURL, nil)
3534
} else if let existingImageOperations = downloadQueue.operations as? [ImageOperation],
3635
let imgOperation = existingImageOperations.first(where: {
3736
return ($0.imageURL == imageURL) && $0.isExecuting && !$0.isFinished
@@ -44,9 +43,9 @@ final class ImageDownloader {
4443
switch result {
4544
case let .success(image):
4645
self.imageCache.setObject(image, forKey: imageURL.absoluteString as NSString)
47-
completion(image, indexPath, imageURL, nil)
46+
completion(image, false, imageURL, nil)
4847
case let .failure(error):
49-
completion(nil, indexPath, imageURL, error)
48+
completion(nil, false, imageURL, error)
5049
}
5150
}
5251
downloadQueue.addOperation(imageOperation)
@@ -63,7 +62,7 @@ final class ImageDownloader {
6362
guard let operation = imageOperations.first else {
6463
return
6564
}
66-
operation.queuePriority = .low
65+
operation.queuePriority = .normal
6766
}
6867

6968
func cancelAll() {

FlickrSearchApp/Modules/FlickrPhotoDetail/View/FlickrPhotoDetailViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ final class FlickrPhotoDetailViewController: UIViewController, FlickrPhotoDetail
6363
}
6464

6565
func renderView(with imageUrl: URL) {
66-
ImageDownloader.shared.downloadImage(withURL: imageUrl, size: view.bounds.size, indexPath: nil) { [weak self] (image, _, _, error) in
66+
ImageDownloader.shared.downloadImage(withURL: imageUrl, size: view.bounds.size) { [weak self] (image, _, _, error) in
6767
DispatchQueue.main.async {
6868
guard let photoImageView = self?.photoImageView, let image = image else {
6969
return

FlickrSearchApp/Modules/FllickrSearch/Entity/FlickrPhotos.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
//
2-
// FlickrPhotos.swift
3-
// FlickrSearchApp
4-
//
5-
// Created by Suhit Patil on 03/10/18.
6-
// Copyright © 2018 Suhit Patil. All rights reserved.
7-
//
8-
91
import Foundation
102

113
struct FlickrPhotos: Decodable {

FlickrSearchApp/Modules/FllickrSearch/View/FlickrImageCell.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,7 @@ final class FlickrImageCell: UICollectionViewCell, Reusable {
3333
photoImageView.edges(to: self)
3434
}
3535

36-
override func prepareForReuse() {
37-
super.prepareForReuse()
38-
photoImageView.image = UIImage(color: .black)
39-
}
40-
4136
func configure(imageURL: URL, size: CGSize, indexPath: IndexPath) {
42-
photoImageView.loadImage(with: imageURL, size: size, indexPath: indexPath)
37+
photoImageView.loadImage(with: imageURL, size: size)
4338
}
4439
}

0 commit comments

Comments
 (0)