root / Classes / AddPhotoViewController.m @ 29cc4957
History | View | Annotate | Download (18 kB)
1 |
// |
---|---|
2 |
// AddPhotoViewController.m |
3 |
// OpenStack |
4 |
// |
5 |
// Created by Mike Mayo on 1/5/11. |
6 |
// Copyright 2011 __MyCompanyName__. All rights reserved. |
7 |
// |
8 |
|
9 |
#import "AddPhotoViewController.h" |
10 |
#import "UIViewController+Conveniences.h" |
11 |
#import "UIColor+MoreColors.h" |
12 |
#import "Container.h" |
13 |
#import "ActivityIndicatorView.h" |
14 |
#import "StorageObject.h" |
15 |
#import "OpenStackAccount.h" |
16 |
#import "AccountManager.h" |
17 |
#import "Container.h" |
18 |
#import "Folder.h" |
19 |
#import "FolderViewController.h" |
20 |
#import "NSObject+Conveniences.h" |
21 |
#import "APICallback.h" |
22 |
#import "ComputeModel.h" |
23 |
|
24 |
#define kName 0 |
25 |
|
26 |
// give me the option to add a description of the file and the URL - date/time stamped |
27 |
|
28 |
@implementation AddPhotoViewController |
29 |
|
30 |
@synthesize image, account, container, folder, folderViewController, isFromCamera; |
31 |
|
32 |
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { |
33 |
return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait); |
34 |
} |
35 |
|
36 |
#pragma mark - |
37 |
#pragma mark View lifecycle |
38 |
|
39 |
- (void)viewDidLoad { |
40 |
[super viewDidLoad]; |
41 |
self.navigationItem.title = @"Add Photo"; |
42 |
[self addSaveButton]; |
43 |
format = @".jpg"; |
44 |
|
45 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
46 |
nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(98.0, 13.0, 400.0, 24.0)]; |
47 |
} else { |
48 |
nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(79.0, 13.0, 222.0, 24.0)]; |
49 |
} |
50 |
nameTextField.delegate = self; |
51 |
nameTextField.font = [UIFont systemFontOfSize:17.0]; |
52 |
nameTextField.textColor = [UIColor value1DetailTextLabelColor]; |
53 |
nameTextField.backgroundColor = [UIColor clearColor]; |
54 |
nameTextField.textAlignment = UITextAlignmentRight; |
55 |
nameTextField.returnKeyType = UIReturnKeyDone; |
56 |
nameTextField.autocorrectionType = UITextAutocorrectionTypeNo; |
57 |
nameTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; |
58 |
nameTextField.placeholder = [NSString stringWithFormat:@"ios_upload_%.0f", [[NSDate date] timeIntervalSince1970]]; |
59 |
|
60 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
61 |
formatLabel = [[UILabel alloc] initWithFrame:CGRectMake(41.0, 15.5, 458.0, 18.0)]; |
62 |
} else { |
63 |
formatLabel = [[UILabel alloc] initWithFrame:CGRectMake(21.0, 15.5, 280.0, 18.0)]; |
64 |
} |
65 |
formatLabel.font = [UIFont systemFontOfSize:17.0]; |
66 |
formatLabel.textColor = [UIColor value1DetailTextLabelColor]; |
67 |
formatLabel.backgroundColor = [UIColor clearColor]; |
68 |
formatLabel.textAlignment = UITextAlignmentRight; |
69 |
formatLabel.text = format; |
70 |
|
71 |
slider = [[UISlider alloc] init]; |
72 |
slider.value = 0.65; |
73 |
|
74 |
// move the text field to make room for the numbers label |
75 |
CGSize size = [formatLabel.text sizeWithFont:formatLabel.font constrainedToSize:CGSizeMake(280.0, 900.0f)]; |
76 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
77 |
nameTextField.frame = CGRectMake(98.0, 13.0, 400.0 - size.width, 24.0); |
78 |
} else { |
79 |
nameTextField.frame = CGRectMake(79.0, 13.0, 222.0 - size.width, 24.0); |
80 |
} |
81 |
|
82 |
} |
83 |
|
84 |
- (void)viewWillAppear:(BOOL)animated { |
85 |
[super viewWillAppear:animated]; |
86 |
// data = UIImageJPEGRepresentation(image, slider.value); |
87 |
// [data retain]; |
88 |
|
89 |
// when saving a camera image as a PNG, it ends up rotated. i have no idea why and have |
90 |
// not been able to successfully rotate the image, so we're only allowing JPEG from the |
91 |
// camera for now. not a big deal, since the iPhone 4 camera is making 8 MB PNG files |
92 |
if (isFromCamera) { |
93 |
formatSection = -1; |
94 |
qualitySection = 1; |
95 |
} else { |
96 |
formatSection = 1; |
97 |
qualitySection = 2; |
98 |
} |
99 |
} |
100 |
|
101 |
- (void)viewDidAppear:(BOOL)animated { |
102 |
[super viewDidAppear:animated]; |
103 |
[qualityActivityIndicatorView release]; |
104 |
qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Calculating size..."] text:@"Calculating size..."]; |
105 |
[qualityActivityIndicatorView addToView:self.view]; |
106 |
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO]; |
107 |
} |
108 |
|
109 |
#pragma mark - |
110 |
#pragma mark Table view data source |
111 |
|
112 |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { |
113 |
if ([format isEqualToString:@".jpg"]) { |
114 |
if (transitioning) { |
115 |
return isFromCamera ? 1 : 2; |
116 |
} else { |
117 |
return isFromCamera ? 2 : 3; |
118 |
} |
119 |
} else { |
120 |
return transitioning ? 3 : 2; |
121 |
} |
122 |
} |
123 |
|
124 |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
125 |
if (section == kName) { |
126 |
return 2; |
127 |
} else if (section == formatSection) { |
128 |
return 2; |
129 |
} else if (section == qualitySection) { |
130 |
return 1; |
131 |
} else { |
132 |
return 0; |
133 |
} |
134 |
} |
135 |
|
136 |
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { |
137 |
if (indexPath.section == qualitySection) { |
138 |
return tableView.rowHeight + slider.frame.size.height + 3.0; |
139 |
} else { |
140 |
return tableView.rowHeight; |
141 |
} |
142 |
} |
143 |
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { |
144 |
if (section == formatSection) { |
145 |
return @"JPEG is a lossy format designed for digital photography. PNG is a lossless format that supports transparency."; |
146 |
} else if (section == qualitySection) { |
147 |
return @"A high quality will produce a better image. A low quality will use less space."; |
148 |
} else { |
149 |
return @""; |
150 |
} |
151 |
} |
152 |
|
153 |
- (UITableViewCell *)tableView:(UITableView *)tableView nameCellForRowAtIndexPath:(NSIndexPath *)indexPath { |
154 |
static NSString *CellIdentifier = @"NameCell"; |
155 |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
156 |
if (cell == nil) { |
157 |
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease]; |
158 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
159 |
cell.textLabel.text = @"Name"; |
160 |
[cell addSubview:nameTextField]; |
161 |
[cell addSubview:formatLabel]; |
162 |
} |
163 |
return cell; |
164 |
} |
165 |
|
166 |
- (UITableViewCell *)tableView:(UITableView *)tableView qualityCellForRowAtIndexPath:(NSIndexPath *)indexPath { |
167 |
static NSString *CellIdentifier = @"QualityCell"; |
168 |
|
169 |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
170 |
if (cell == nil) { |
171 |
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease]; |
172 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
173 |
|
174 |
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 13.0, 280.0, 20.0)]; |
175 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
176 |
textLabel.frame = CGRectMake(41.0, 13.0, 458.0, 20.0); |
177 |
} |
178 |
textLabel.font = [UIFont boldSystemFontOfSize:17.0]; |
179 |
textLabel.text = @"Quality"; |
180 |
textLabel.textColor = [UIColor blackColor]; |
181 |
textLabel.backgroundColor = [UIColor clearColor]; |
182 |
[cell addSubview:textLabel]; |
183 |
[textLabel release]; |
184 |
|
185 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
186 |
slider.frame = CGRectMake(41.0, 38.0, 458.0, slider.frame.size.height); |
187 |
} else { |
188 |
slider.frame = CGRectMake(20.0, 38.0, 280.0, slider.frame.size.height); |
189 |
} |
190 |
|
191 |
[slider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged]; |
192 |
[slider addTarget:self action:@selector(sliderFinished:) forControlEvents:UIControlEventTouchUpInside]; |
193 |
|
194 |
[cell addSubview:slider]; |
195 |
qualityLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 14.0, 280.0, 18.0)]; |
196 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
197 |
qualityLabel.frame = CGRectMake(41.0, 14.0, 458.0, 18.0); |
198 |
} |
199 |
qualityLabel.font = [UIFont systemFontOfSize:17.0]; |
200 |
qualityLabel.textColor = [UIColor value1DetailTextLabelColor]; |
201 |
qualityLabel.backgroundColor = [UIColor clearColor]; |
202 |
qualityLabel.textAlignment = UITextAlignmentRight; |
203 |
[cell addSubview:qualityLabel]; |
204 |
} |
205 |
|
206 |
qualityLabel.text = [NSString stringWithFormat:@"%.0f%%", slider.value * 100]; |
207 |
|
208 |
return cell; |
209 |
} |
210 |
|
211 |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
212 |
|
213 |
if (indexPath.section == kName && indexPath.row == 0) { |
214 |
return [self tableView:tableView nameCellForRowAtIndexPath:indexPath]; |
215 |
} else if (indexPath.section == qualitySection) { |
216 |
return [self tableView:tableView qualityCellForRowAtIndexPath:indexPath]; |
217 |
} else { |
218 |
static NSString *CellIdentifier = @"Cell"; |
219 |
|
220 |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
221 |
if (cell == nil) { |
222 |
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease]; |
223 |
} |
224 |
|
225 |
if (indexPath.section == kName) { |
226 |
cell.textLabel.text = @"Size"; |
227 |
cell.detailTextLabel.text = [Container humanizedBytes:[data length]]; |
228 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
229 |
cell.accessoryType = UITableViewCellAccessoryNone; |
230 |
} else if (indexPath.section == formatSection) { |
231 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
232 |
if (indexPath.row == 0) { |
233 |
cell.textLabel.text = @"JPEG"; |
234 |
cell.detailTextLabel.text = @""; |
235 |
if ([format isEqualToString:@".jpg"]) { |
236 |
cell.accessoryType = UITableViewCellAccessoryCheckmark; |
237 |
} else { |
238 |
cell.accessoryType = UITableViewCellAccessoryNone; |
239 |
} |
240 |
} else if (indexPath.row == 1) { |
241 |
cell.textLabel.text = @"PNG"; |
242 |
cell.detailTextLabel.text = @""; |
243 |
if ([format isEqualToString:@".png"]) { |
244 |
cell.accessoryType = UITableViewCellAccessoryCheckmark; |
245 |
} else { |
246 |
cell.accessoryType = UITableViewCellAccessoryNone; |
247 |
} |
248 |
} |
249 |
} else { |
250 |
cell.textLabel.text = @"Quality"; |
251 |
cell.detailTextLabel.text = @"65%"; |
252 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
253 |
} |
254 |
|
255 |
return cell; |
256 |
} |
257 |
} |
258 |
|
259 |
#pragma mark - |
260 |
#pragma mark Table view delegate |
261 |
|
262 |
- (void)updateFormat { |
263 |
|
264 |
transitioning = YES; |
265 |
|
266 |
NSArray *indexPaths = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:formatSection], [NSIndexPath indexPathForRow:1 inSection:formatSection], nil]; |
267 |
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; |
268 |
|
269 |
transitioning = NO; |
270 |
|
271 |
if ([format isEqualToString:@".jpg"]) { |
272 |
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:qualitySection] withRowAnimation:UITableViewRowAnimationBottom]; |
273 |
} else { |
274 |
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:qualitySection] withRowAnimation:UITableViewRowAnimationTop]; |
275 |
} |
276 |
|
277 |
formatLabel.text = format; |
278 |
// move the text field to make room for the numbers label |
279 |
CGSize size = [formatLabel.text sizeWithFont:formatLabel.font constrainedToSize:CGSizeMake(280.0, 900.0f)]; |
280 |
nameTextField.frame = CGRectMake(79.0, 13.0, 222.0 - size.width, 24.0); |
281 |
[qualityActivityIndicatorView release]; |
282 |
qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Calculating size..."] text:@"Calculating size..."]; |
283 |
[qualityActivityIndicatorView addToView:self.view]; |
284 |
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO]; |
285 |
|
286 |
} |
287 |
|
288 |
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { |
289 |
if (indexPath.section == formatSection) { |
290 |
if (indexPath.row == 0) { |
291 |
format = @".jpg"; |
292 |
} else { //if (indexPath.row == 1) { |
293 |
format = @".png"; |
294 |
} |
295 |
// [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
296 |
[NSTimer scheduledTimerWithTimeInterval:0.35 target:self selector:@selector(updateFormat) userInfo:nil repeats:NO]; |
297 |
} |
298 |
} |
299 |
|
300 |
#pragma mark - |
301 |
#pragma mark Text Field Delegate |
302 |
|
303 |
- (BOOL)textFieldShouldReturn:(UITextField *)textField { |
304 |
[textField resignFirstResponder]; |
305 |
return NO; |
306 |
} |
307 |
|
308 |
#pragma mark - |
309 |
#pragma mark Slider |
310 |
|
311 |
- (void)calculateSize { |
312 |
if ([format isEqualToString:@".jpg"]) { |
313 |
[data release]; |
314 |
data = UIImageJPEGRepresentation(image, slider.value); |
315 |
[data retain]; |
316 |
} else { |
317 |
[data release]; |
318 |
|
319 |
if (isFromCamera) { |
320 |
// PNG files from the camera need to be rotated |
321 |
UIImage *rotatedImage = [[UIImage alloc] initWithCGImage:(CGImageRef)image scale:1.0 orientation:UIImageOrientationLeft]; |
322 |
data = UIImagePNGRepresentation(rotatedImage); |
323 |
[rotatedImage release]; |
324 |
} else { |
325 |
data = UIImagePNGRepresentation(image); |
326 |
} |
327 |
[data retain]; |
328 |
} |
329 |
|
330 |
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:1 inSection:kName]] withRowAnimation:UITableViewRowAnimationNone]; |
331 |
[qualityActivityIndicatorView removeFromSuperview]; |
332 |
} |
333 |
|
334 |
- (void)sliderFinished:(id)sender { |
335 |
[qualityActivityIndicatorView release]; |
336 |
qualityActivityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Calculating size..."] text:@"Calculating size..."]; |
337 |
[qualityActivityIndicatorView addToView:self.view]; |
338 |
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(calculateSize) userInfo:nil repeats:NO]; |
339 |
} |
340 |
|
341 |
- (void)sliderMoved:(id)sender { |
342 |
qualityLabel.text = [NSString stringWithFormat:@"%.0f%%", slider.value * 100]; |
343 |
} |
344 |
|
345 |
#pragma mark - |
346 |
#pragma mark Save Button |
347 |
|
348 |
- (void)saveButtonPressed:(id)sender { |
349 |
[nameTextField resignFirstResponder]; |
350 |
NSString *objectName; |
351 |
if (nameTextField.text && ![nameTextField.text isEqualToString:@""]) { |
352 |
objectName = [NSString stringWithFormat:@"%@%@", nameTextField.text, format]; |
353 |
} else { |
354 |
objectName = [NSString stringWithFormat:@"%@%@", nameTextField.placeholder, format]; |
355 |
} |
356 |
|
357 |
BOOL objectNameHasTrailingSlash = [objectName hasSuffix:@"/"]; |
358 |
NSUInteger indexOfFirstSlash = [objectName rangeOfString:@"/"].location; |
359 |
|
360 |
if ((!objectNameHasTrailingSlash && indexOfFirstSlash != NSNotFound) || |
361 |
(objectNameHasTrailingSlash && indexOfFirstSlash < objectName.length - 1)) { |
362 |
[self alert:@"Invalid file name" message:@"'/' characters are only allowed at the end of file names"]; |
363 |
return; |
364 |
} |
365 |
|
366 |
if ([folderViewController.folder.objects objectForKey:objectName] && !allowOverwrite) { |
367 |
NSString *alertMessage = [NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?",objectName, folderViewController.container.name]; |
368 |
NSString *alertTitle = @"Apply changes"; |
369 |
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle |
370 |
message:alertMessage |
371 |
delegate:self |
372 |
cancelButtonTitle:@"Cancel" |
373 |
otherButtonTitles:@"OK", nil]; |
374 |
[alert show]; |
375 |
[alert release]; |
376 |
return; |
377 |
} |
378 |
[activityIndicatorView release]; |
379 |
activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:@"Uploading..." withProgress:YES] text:@"Uploading..." withProgress:YES]; |
380 |
[activityIndicatorView addToView:self.view]; |
381 |
|
382 |
StorageObject *object = [[StorageObject alloc] init]; |
383 |
object.name = objectName; |
384 |
object.fullPath = [NSString stringWithFormat:@"%@/%@", [folder fullPath], object.name]; |
385 |
object.fullPath = [object.fullPath substringFromIndex:1]; |
386 |
|
387 |
if ([format isEqualToString:@".jpg"]) { |
388 |
object.contentType = @"image/jpeg"; |
389 |
} else { |
390 |
object.contentType = @"image/png"; |
391 |
} |
392 |
|
393 |
object.data = data; |
394 |
object.bytes = [data length]; |
395 |
|
396 |
[[self.account.manager writeObject:self.container object:object downloadProgressDelegate:activityIndicatorView.progressView] |
397 |
success:^(OpenStackRequest *request) { |
398 |
[activityIndicatorView removeFromSuperview]; |
399 |
object.data = nil; |
400 |
object.sharing = folder.sharing; |
401 |
object.lastModified = [ComputeModel dateFromRFC1123String:[request.responseHeaders objectForKey:@"Date"]]; |
402 |
BOOL currentFolderIsRoot = NO; |
403 |
if ([folderViewController.folder isEqual:container.rootFolder]) { |
404 |
currentFolderIsRoot = YES; |
405 |
} |
406 |
[folder.objects setObject:object forKey:object.name]; |
407 |
[folderViewController.tableView reloadData]; |
408 |
if (currentFolderIsRoot) { |
409 |
container.count += 1; |
410 |
container.rootFolder = folder; |
411 |
[self.account.containers setObject:container forKey:container.name]; |
412 |
} |
413 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) |
414 |
[folderViewController setDetailViewController]; |
415 |
allowOverwrite = NO; |
416 |
[self dismissModalViewControllerAnimated:YES]; |
417 |
} |
418 |
failure:^(OpenStackRequest *request) { |
419 |
[activityIndicatorView removeFromSuperview]; |
420 |
allowOverwrite = NO; |
421 |
[self alert:@"There was a problem uploading this file." request:request]; |
422 |
}]; |
423 |
} |
424 |
|
425 |
#pragma mark - Alertview delegate |
426 |
|
427 |
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex |
428 |
{ |
429 |
if (buttonIndex == 1) { |
430 |
allowOverwrite = YES; |
431 |
[self saveButtonPressed:nil]; |
432 |
} |
433 |
} |
434 |
|
435 |
#pragma mark - |
436 |
#pragma mark Memory management |
437 |
|
438 |
- (void)dealloc { |
439 |
[image release]; |
440 |
[account release]; |
441 |
[container release]; |
442 |
[folder release]; |
443 |
[folderViewController release]; |
444 |
[nameTextField release]; |
445 |
[formatLabel release]; |
446 |
[slider release]; |
447 |
[qualityActivityIndicatorView release]; |
448 |
[activityIndicatorView release]; |
449 |
[super dealloc]; |
450 |
} |
451 |
|
452 |
|
453 |
@end |
454 |
|