forked from facebook/facebook-ios-sdk
/
FBProfilePictureView.m
236 lines (188 loc) · 7.56 KB
/
FBProfilePictureView.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
* Copyright 2010-present Facebook.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FBProfilePictureView.h"
#import "FBURLConnection.h"
#import "FBRequest.h"
#import "FBUtility.h"
#import "FBSDKVersion.h"
#import "FBProfilePictureViewBlankProfilePortraitPNG.h"
#import "FBProfilePictureViewBlankProfileSquarePNG.h"
@interface FBProfilePictureView()
@property (readonly, nonatomic) NSString *imageQueryParamString;
@property (retain, nonatomic) NSString *previousImageQueryParamString;
@property (retain, nonatomic) FBURLConnection *connection;
@property (retain, nonatomic) UIImageView *imageView;
- (void)initialize;
- (void)refreshImage:(BOOL)forceRefresh;
- (void)ensureImageViewContentMode;
@end
@implementation FBProfilePictureView
@synthesize profileID = _profileID;
@synthesize pictureCropping = _pictureCropping;
@synthesize connection = _connection;
@synthesize imageView = _imageView;
@synthesize previousImageQueryParamString = _previousImageQueryParamString;
#pragma mark - Lifecycle
- (void)dealloc {
[_profileID release];
[_imageView release];
[_connection release];
[_previousImageQueryParamString release];
[super dealloc];
}
- (id)init {
self = [super init];
if (self) {
[self initialize];
}
return self;
}
- (id)initWithProfileID:(NSString *)profileID
pictureCropping:(FBProfilePictureCropping)pictureCropping {
self = [self init];
if (self) {
self.pictureCropping = pictureCropping;
self.profileID = profileID;
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initialize];
}
return self;
}
#pragma mark -
- (NSString *)imageQueryParamString {
static CGFloat screenScaleFactor = 0.0;
if (screenScaleFactor == 0.0) {
screenScaleFactor = [[UIScreen mainScreen] scale];
}
// Retina display doesn't increase the bounds that iOS returns. The larger size to fetch needs
// to be calculated using the scale factor accessed above.
int width = (int)(self.bounds.size.width * screenScaleFactor);
if (self.pictureCropping == FBProfilePictureCroppingSquare) {
return [NSString stringWithFormat:@"width=%d&height=%d&migration_bundle=%@",
width,
width,
FB_IOS_SDK_MIGRATION_BUNDLE];
}
// For non-square images, we choose between three variants knowing that the small profile picture is
// 50 pixels wide, normal is 100, and large is about 200.
if (width <= 50) {
return @"type=small";
} else if (width <= 100) {
return @"type=normal";
} else {
return @"type=large";
}
}
- (void)initialize {
// the base class can cause virtual recursion, so
// to handle this we make initialize idempotent
if (self.imageView) {
return;
}
UIImageView* imageView = [[[UIImageView alloc] initWithFrame:self.bounds] autorelease];
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.imageView = imageView;
self.autoresizesSubviews = YES;
self.clipsToBounds = YES;
[self addSubview:self.imageView];
}
- (void)refreshImage:(BOOL)forceRefresh {
NSString *newImageQueryParamString = self.imageQueryParamString;
// If not forcing refresh, check to see if the previous size we used would be the same
// as what we'd request now, as this method could be called often on control bounds animation,
// and we only want to fetch when needed.
if (!forceRefresh && [self.previousImageQueryParamString isEqualToString:newImageQueryParamString]) {
// But we still may need to adjust the contentMode.
[self ensureImageViewContentMode];
return;
}
if (self.profileID) {
[self.connection cancel];
FBURLConnectionHandler handler =
^(FBURLConnection *connection, NSError *error, NSURLResponse *response, NSData *data) {
FBConditionalLog(self.connection == connection, @"Inconsistent connection state");
self.connection = nil;
if (!error) {
self.imageView.image = [UIImage imageWithData:data];
[self ensureImageViewContentMode];
}
};
NSString *template = @"%@/%@/picture?%@";
NSString *urlString = [NSString stringWithFormat:template,
[FBUtility buildFacebookUrlWithPre:@"https://graph."],
self.profileID,
newImageQueryParamString];
NSURL *url = [NSURL URLWithString:urlString];
self.connection = [[[FBURLConnection alloc] initWithURL:url
completionHandler:handler]
autorelease];
} else {
BOOL isSquare = (self.pictureCropping == FBProfilePictureCroppingSquare);
self.imageView.image = isSquare ?
[FBProfilePictureViewBlankProfileSquarePNG image] :
[FBProfilePictureViewBlankProfilePortraitPNG image];
[self ensureImageViewContentMode];
}
self.previousImageQueryParamString = newImageQueryParamString;
}
- (void)ensureImageViewContentMode {
// Set the image's contentMode such that if the image is larger than the control, we scale it down, preserving aspect
// ratio. Otherwise, we center it. This ensures that we never scale up, and pixellate, the image.
CGSize viewSize = self.bounds.size;
CGSize imageSize = self.imageView.image.size;
UIViewContentMode contentMode;
// If both of the view dimensions are larger than the image, we'll center the image to prevent scaling up.
// Note that unlike in choosing the image size, we *don't* use any Retina-display scaling factor to choose centering
// vs. filling. If we were to do so, we'd get profile pics shrinking to fill the the view on non-Retina, but getting
// centered and clipped on Retina.
if (viewSize.width > imageSize.width && viewSize.height > imageSize.height) {
contentMode = UIViewContentModeCenter;
} else {
contentMode = UIViewContentModeScaleAspectFit;
}
self.imageView.contentMode = contentMode;
}
- (void)setProfileID:(NSString*)profileID {
if (!_profileID || ![_profileID isEqualToString:profileID]) {
[_profileID release];
_profileID = [profileID copy];
[self refreshImage:YES];
}
}
- (void)setPictureCropping:(FBProfilePictureCropping)pictureCropping {
if (_pictureCropping != pictureCropping) {
_pictureCropping = pictureCropping;
[self refreshImage:YES];
}
}
// Lets us catch resizes of the control, or any outer layout, allowing us to potentially
// choose a different image.
- (void)layoutSubviews {
[self refreshImage:NO];
[super layoutSubviews];
}
@end