-
Notifications
You must be signed in to change notification settings - Fork 1
/
MYSImageView+URL.m
149 lines (111 loc) · 4.34 KB
/
MYSImageView+URL.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//
// FSImageView+URL.m
// FirehoseChat
//
// Created by Adam Kirk on 5/13/14.
// Copyright (c) 2014 Adam Kirk. All rights reserved.
//
#import "MYSImageView+URL.h"
#import <objc/runtime.h>
static char imageURLKey;
static NSInteger maxAttemptCount = 3;
@implementation MYSImageView (URL)
+ (NSCache *)sharedImageCache
{
static NSCache *__cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__cache = [NSCache new];
});
return __cache;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setImageWithContentsOfURL:(NSURL *)URL placeholder:(MYSImage *)placeholderImage
{
if (!URL) return;
// set the placeholder image so we have somethign there while we figure out if we can set the intended iamge.
if (placeholderImage) {
[self setImage:placeholderImage];
}
else {
MYSImage *defaultPlaceholder = [MYSImage imageNamed:@"icon_noavatar"];
if (defaultPlaceholder) {
[self setImage:defaultPlaceholder];
}
}
// set the image url so we know what image, when loaded, should be set on this image view
[self setImageURL:URL];
// register to know when the image is done loading
[self registerForNotifications];
// this value will either be nil, NSNull or NSImage. We'll handle each case below
id imageOrNull = [[[self class] sharedImageCache] objectForKey:URL];
// if the image is loaded, just set it and we're done
if ([imageOrNull isKindOfClass:[MYSImage class]]) {
[self setImage:imageOrNull];
return;
}
// just return because the image with this url is currently loading, we'll get a notif when it's ready
else if ([imageOrNull isKindOfClass:[NSNull class]]) {
return;
}
// if it's nil, we need to be the instance that loads the image. We'll notify all other image views with this url when it's done.
else {
// mark that this URL has already started loading an image.
[[[self class] sharedImageCache] setObject:[NSNull null] forKey:URL];
// try to load the image 3 times before giving up.
[self loadImageAtURL:URL attemptCount:1];
}
}
#pragma mark - Properties
- (void)setImageURL:(NSURL *)imageURL
{
objc_setAssociatedObject(self, &imageURLKey, imageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSURL *)imageURL
{
return objc_getAssociatedObject(self, &imageURLKey);
}
#pragma mark - Notifications
- (void)imageDidLoad:(NSNotification *)note
{
MYSImage *image = note.userInfo[@"ImageKey"];
NSURL *imageURL = note.userInfo[@"ImageURL"];
// its possible that a second URL was set on this image view before the first URL finished loading it's image. If that's the case,
// don't set it as the image.
if ([[self imageURL] isEqual:imageURL]) {
[self setImage:image];
}
}
#pragma mark - Private
- (void)registerForNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(imageDidLoad:)
name:MYSImageViewImageDidLoadFromURLNotification
object:[self imageURL]];
}
- (void)loadImageAtURL:(NSURL *)URL attemptCount:(NSInteger)attemptCount
{
if (attemptCount <= maxAttemptCount) {
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:URL]
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
MYSImage *image = [[MYSImage alloc] initWithData:data];
if (image) {
[[[self class] sharedImageCache] setObject:image forKey:URL];
[[NSNotificationCenter defaultCenter] postNotificationName:MYSImageViewImageDidLoadFromURLNotification
object:URL
userInfo:@{ @"ImageKey" : image, @"ImageURL" : URL }];
}
else {
[self loadImageAtURL:URL attemptCount:attemptCount + 1];
}
}];
}
}
@end
NSString * const MYSImageViewImageDidLoadFromURLNotification = @"MYSImageViewImageDidLoadFromURLNotification";