This repository has been archived by the owner on Mar 9, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 89b3e75
Showing
17 changed files
with
1,900 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
.DS_Store | ||
*.swp | ||
*.pbxuser | ||
*.perspectivev3 | ||
*.mode1v3 | ||
xcuserdata/ | ||
build/ | ||
Documentation/ | ||
Frameworks/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
|
||
/* | ||
File: Event.h | ||
Abstract: A class to represent an event containing geographical coordinates, a time stamp, and an image with a thumbnail. Every Event has a CouchDB document. | ||
Version: 1.1 | ||
*/ | ||
|
||
#import <CoreLocation/CoreLocation.h> | ||
@class CouchDatabase, CouchDocument; | ||
|
||
@interface Event : NSObject { | ||
CouchDocument* document; | ||
UIImage* photo; | ||
UIImage* thumbnail; | ||
BOOL checkedForPhoto; | ||
} | ||
|
||
/** Creates a brand-new event and adds a document for it to the database. */ | ||
- (id)initWithDatabase:(CouchDatabase*)database | ||
latitude:(CLLocationDegrees)latitude | ||
longitude:(CLLocationDegrees)longitude | ||
creationDate:(NSDate*)creationDate; | ||
|
||
/** Instantiates an Event object for an already-existing document. */ | ||
- (id)initWithDocument:(CouchDocument*)document; | ||
|
||
/** Deletes the event's document from the database permanently. */ | ||
- (void) deleteDocument; | ||
|
||
@property (nonatomic, readonly) NSDate *creationDate; | ||
@property (nonatomic, readonly) NSNumber* latitude; | ||
@property (nonatomic, readonly) NSNumber* longitude; | ||
|
||
@property (nonatomic, retain) UIImage *photo; | ||
@property (nonatomic, readonly) UIImage *thumbnail; | ||
|
||
@end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
|
||
/* | ||
File: Event.m | ||
Abstract: A class to represent an event containing geographical coordinates, a time stamp, and an image with a thumbnail. Every Event has a CouchDB document. | ||
Version: 1.1 | ||
*/ | ||
|
||
#import "Event.h" | ||
|
||
#import <CouchCocoa/CouchCocoa.h> | ||
#import <CouchCocoa/RESTBody.h> | ||
|
||
|
||
static UIImage* MakeThumbnail(UIImage* image, CGFloat dimensions); | ||
|
||
|
||
@implementation Event | ||
|
||
|
||
- (id)initWithDatabase:(CouchDatabase*)database | ||
latitude:(CLLocationDegrees)latitude | ||
longitude:(CLLocationDegrees)longitude | ||
creationDate:(NSDate*)creationDate { | ||
self = [super init]; | ||
if (self) { | ||
NSDictionary* properties = [NSDictionary dictionaryWithObjectsAndKeys: | ||
[NSNumber numberWithDouble: latitude], @"latitude", | ||
[NSNumber numberWithDouble: longitude], @"longitude", | ||
[RESTBody JSONObjectWithDate: creationDate], @"creationDate", | ||
nil]; | ||
document = [[database untitledDocument] retain]; | ||
RESTOperation* op = [document putProperties: properties]; | ||
if (![op wait]) { | ||
// TODO: report error | ||
NSLog(@"Creating Event document failed! %@", op.error); | ||
} | ||
} | ||
return self; | ||
} | ||
|
||
|
||
- (id)initWithDocument:(CouchDocument*)aDocument { | ||
self = [super init]; | ||
if (self) { | ||
document = [aDocument retain]; | ||
} | ||
return self; | ||
} | ||
|
||
|
||
- (void)dealloc { | ||
[document release]; | ||
[thumbnail release]; | ||
[super dealloc]; | ||
} | ||
|
||
|
||
- (void) deleteDocument { | ||
[[document DELETE] start]; | ||
[document release]; | ||
document = nil; | ||
} | ||
|
||
|
||
- (NSDate*) creationDate { | ||
NSString* dateString = [document.properties objectForKey: @"creationDate"]; | ||
return [RESTBody dateWithJSONObject: dateString]; | ||
} | ||
|
||
|
||
- (NSNumber*) latitude { | ||
return [document.properties objectForKey: @"latitude"]; | ||
} | ||
|
||
|
||
- (NSNumber*) longitude { | ||
return [document.properties objectForKey: @"longitude"]; | ||
} | ||
|
||
|
||
- (UIImage*)thumbnail { | ||
// The thumbnail is stored inline as Base64-encoded data, because it's small. | ||
if (!thumbnail) { | ||
NSString* thumbnailBase64 = [document.properties objectForKey: @"thumbnail"]; | ||
NSData* thumbnailData = [RESTBody dataWithBase64: thumbnailBase64]; | ||
if (thumbnailData) { | ||
thumbnail = [[UIImage alloc] initWithData: thumbnailData]; | ||
} | ||
} | ||
return thumbnail; | ||
} | ||
|
||
|
||
- (UIImage*)photo { | ||
// The photo is stored as an attachment, so it is fetched separately on demand. | ||
if (!checkedForPhoto) { | ||
checkedForPhoto = YES; | ||
CouchAttachment* attach = [document.currentRevision attachmentNamed: @"photo"]; | ||
RESTOperation* op = [attach GET]; | ||
if ([op isSuccessful]) { | ||
NSData* imageData = op.responseBody.content; | ||
photo = [[UIImage alloc] initWithData: imageData]; | ||
} | ||
} | ||
return photo; | ||
} | ||
|
||
|
||
- (void)setPhoto:(UIImage *)newPhoto { | ||
if (checkedForPhoto && newPhoto == photo) | ||
return; | ||
[photo release]; | ||
photo = [newPhoto retain]; | ||
checkedForPhoto = YES; | ||
|
||
// Update the thumbnail image: | ||
[thumbnail release]; | ||
thumbnail = nil; | ||
NSData* thumbnailData = nil; | ||
if (newPhoto) { | ||
thumbnail = [MakeThumbnail(newPhoto, 44) retain]; | ||
thumbnailData = UIImageJPEGRepresentation(thumbnail, 0.5); | ||
} | ||
|
||
// Save the thumbnail inline: | ||
NSMutableDictionary* properties = [[document.properties mutableCopy] autorelease]; | ||
[properties setValue:[RESTBody base64WithData:thumbnailData] forKey:@"thumbnail"]; | ||
RESTOperation* op = [document putProperties:properties]; | ||
if (![op wait]) { | ||
// TODO: Report error | ||
NSLog(@"Saving Event document failed! %@", op.error); | ||
} | ||
|
||
// Save the photo to an attachment, asynchronously: | ||
CouchAttachment* attach = [document.currentRevision createAttachmentWithName:@"photo" | ||
type:@"image/png"]; | ||
if (newPhoto) { | ||
op = [attach PUT: UIImagePNGRepresentation(newPhoto) contentType: @"image/png"]; | ||
} else { | ||
op = [attach DELETE]; | ||
} | ||
[op onCompletion: ^{ | ||
if (!op.isSuccessful) { | ||
// TODO: Report error | ||
NSLog(@"Saving Event photo attachment failed! %@", op.error); | ||
} | ||
}]; | ||
} | ||
|
||
|
||
@end | ||
|
||
|
||
/** Makes a shrunk-down copy of an image. */ | ||
static UIImage* MakeThumbnail(UIImage* image, CGFloat dimensions) { | ||
CGSize size = image.size; | ||
CGFloat ratio = dimensions / MAX(size.height, size.width); | ||
CGRect rect = CGRectMake(0.0, 0.0, ratio * size.width, ratio * size.height); | ||
|
||
UIGraphicsBeginImageContext(rect.size); | ||
[image drawInRect:rect]; | ||
UIImage* thumbnail = UIGraphicsGetImageFromCurrentImageContext(); | ||
UIGraphicsEndImageContext(); | ||
return thumbnail; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
|
||
/* | ||
File: EventDetailViewController.h | ||
Abstract: The table view controller responsible for displaying the time, coordinates, and photo of an event, and allowing the user to select a photo for the event, or delete the existing photo. | ||
Version: 1.1 | ||
*/ | ||
|
||
@class Event; | ||
|
||
@interface EventDetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate> { | ||
Event *event; | ||
UILabel *timeLabel; | ||
UILabel *coordinatesLabel; | ||
UIButton *deletePhotoButton; | ||
UIImageView *photoImageView; | ||
} | ||
|
||
@property (nonatomic, retain) Event *event; | ||
|
||
@property (nonatomic, retain) IBOutlet UILabel *timeLabel; | ||
@property (nonatomic, retain) IBOutlet UILabel *coordinatesLabel; | ||
@property (nonatomic, retain) IBOutlet UIButton *deletePhotoButton; | ||
@property (nonatomic, retain) IBOutlet UIImageView *photoImageView; | ||
|
||
- (IBAction)choosePhoto; | ||
- (IBAction)deletePhoto; | ||
- (void)updatePhotoInfo; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
|
||
/* | ||
File: EventDetailViewController.m | ||
Abstract: The table view controller responsible for displaying the time, coordinates, and photo of an event, and allowing the user to select a photo for the event, or delete the existing photo. | ||
Version: 1.1 | ||
*/ | ||
|
||
#import "EventDetailViewController.h" | ||
#import "Event.h" | ||
|
||
|
||
@implementation EventDetailViewController | ||
|
||
@synthesize event, timeLabel, coordinatesLabel, deletePhotoButton, photoImageView; | ||
|
||
|
||
#pragma mark - | ||
#pragma mark Lifecycle | ||
|
||
- (void)viewDidLoad { | ||
[super viewDidLoad]; | ||
|
||
// A date formatter for the creation date. | ||
static NSDateFormatter *dateFormatter = nil; | ||
if (dateFormatter == nil) { | ||
dateFormatter = [[NSDateFormatter alloc] init]; | ||
[dateFormatter setTimeStyle:NSDateFormatterShortStyle]; | ||
[dateFormatter setDateStyle:NSDateFormatterMediumStyle]; | ||
} | ||
|
||
static NSNumberFormatter *numberFormatter; | ||
if (numberFormatter == nil) { | ||
numberFormatter = [[NSNumberFormatter alloc] init]; | ||
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; | ||
[numberFormatter setMaximumFractionDigits:3]; | ||
} | ||
|
||
timeLabel.text = [dateFormatter stringFromDate:[event creationDate]]; | ||
|
||
NSString *coordinatesString = [[NSString alloc] initWithFormat:@"%@, %@", | ||
[numberFormatter stringFromNumber:[event latitude]], | ||
[numberFormatter stringFromNumber:[event longitude]]]; | ||
coordinatesLabel.text = coordinatesString; | ||
[coordinatesString release]; | ||
|
||
[self updatePhotoInfo]; | ||
} | ||
|
||
|
||
- (void)viewDidUnload { | ||
|
||
self.timeLabel = nil; | ||
self.coordinatesLabel = nil; | ||
self.deletePhotoButton = nil; | ||
self.photoImageView = nil; | ||
} | ||
|
||
|
||
#pragma mark - | ||
#pragma mark Editing the photo | ||
|
||
- (IBAction)deletePhoto { | ||
[event setPhoto:nil]; | ||
|
||
// Update the user interface appropriately. | ||
[self updatePhotoInfo]; | ||
} | ||
|
||
|
||
- (IBAction)choosePhoto { | ||
|
||
// Show an image picker to allow the user to choose a new photo. | ||
|
||
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; | ||
imagePicker.delegate = self; | ||
[self presentModalViewController:imagePicker animated:YES]; | ||
[imagePicker release]; | ||
} | ||
|
||
|
||
|
||
- (void)updatePhotoInfo { | ||
|
||
// Synchronize the photo image view and the text on the photo button with the event's photo. | ||
UIImage *image = event.photo; | ||
|
||
photoImageView.image = image; | ||
deletePhotoButton.enabled = (image != nil); | ||
} | ||
|
||
|
||
#pragma mark - | ||
#pragma mark Image picker delegate methods | ||
|
||
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)selectedImage editingInfo:(NSDictionary *)editingInfo { | ||
// Update the database. | ||
event.photo = selectedImage; | ||
// Update the user interface appropriately. | ||
[self updatePhotoInfo]; | ||
|
||
[self dismissModalViewControllerAnimated:YES]; | ||
} | ||
|
||
|
||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { | ||
// The user canceled -- simply dismiss the image picker. | ||
[self dismissModalViewControllerAnimated:YES]; | ||
} | ||
|
||
|
||
#pragma mark - | ||
#pragma mark Memory management | ||
|
||
- (void)dealloc { | ||
|
||
[event release]; | ||
[timeLabel release]; | ||
[coordinatesLabel release]; | ||
[deletePhotoButton release]; | ||
[photoImageView release]; | ||
|
||
[super dealloc]; | ||
} | ||
|
||
|
||
@end |
Oops, something went wrong.