From 4efc90a517f7b362aa90d0992ab9f5979e8829f0 Mon Sep 17 00:00:00 2001 From: Alexis Gallagher Date: Fri, 3 Oct 2014 09:25:13 -0700 Subject: [PATCH 1/5] add start of demo project --- RSAvatarController.xcodeproj/project.pbxproj | 6 ++ .../contents.xcworkspacedata | 7 ++ .../RSAvatarController.xccheckout | 41 ++++++++++++ RSAvatarController/DemoViewController.h | 13 ++++ RSAvatarController/DemoViewController.m | 65 +++++++++++++++++++ RSAvatarController/RSAppDelegate.m | 7 +- 6 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 RSAvatarController.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 RSAvatarController.xcodeproj/project.xcworkspace/xcshareddata/RSAvatarController.xccheckout create mode 100644 RSAvatarController/DemoViewController.h create mode 100644 RSAvatarController/DemoViewController.m diff --git a/RSAvatarController.xcodeproj/project.pbxproj b/RSAvatarController.xcodeproj/project.pbxproj index 1108110..e1c5c1d 100644 --- a/RSAvatarController.xcodeproj/project.pbxproj +++ b/RSAvatarController.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 5E5614AE19DEFF9800D88C74 /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E5614AD19DEFF9800D88C74 /* DemoViewController.m */; }; CF2886B9177AB26300B43887 /* RSAvatarController.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2886B8177AB26300B43887 /* RSAvatarController.m */; }; CF2886BF177AB34A00B43887 /* RSMoveAndScaleController.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2886BE177AB34A00B43887 /* RSMoveAndScaleController.m */; }; CF7B83AD17B8E34D00913E5B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CF7B83AC17B8E34D00913E5B /* QuartzCore.framework */; }; @@ -22,6 +23,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 5E5614AC19DEFF9800D88C74 /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + 5E5614AD19DEFF9800D88C74 /* DemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; CF2886B7177AB26300B43887 /* RSAvatarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSAvatarController.h; sourceTree = ""; }; CF2886B8177AB26300B43887 /* RSAvatarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSAvatarController.m; sourceTree = ""; }; CF2886BD177AB34A00B43887 /* RSMoveAndScaleController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSMoveAndScaleController.h; sourceTree = ""; }; @@ -103,6 +106,8 @@ CFA94345177AB13E005782D7 /* RSAppDelegate.h */, CFA94346177AB13E005782D7 /* RSAppDelegate.m */, CFA9433D177AB13E005782D7 /* Supporting Files */, + 5E5614AC19DEFF9800D88C74 /* DemoViewController.h */, + 5E5614AD19DEFF9800D88C74 /* DemoViewController.m */, ); path = RSAvatarController; sourceTree = ""; @@ -190,6 +195,7 @@ CFA94343177AB13E005782D7 /* main.m in Sources */, CF2886BF177AB34A00B43887 /* RSMoveAndScaleController.m in Sources */, CF2886B9177AB26300B43887 /* RSAvatarController.m in Sources */, + 5E5614AE19DEFF9800D88C74 /* DemoViewController.m in Sources */, CFA94347177AB13E005782D7 /* RSAppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/RSAvatarController.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/RSAvatarController.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..76e9877 --- /dev/null +++ b/RSAvatarController.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/RSAvatarController.xcodeproj/project.xcworkspace/xcshareddata/RSAvatarController.xccheckout b/RSAvatarController.xcodeproj/project.xcworkspace/xcshareddata/RSAvatarController.xccheckout new file mode 100644 index 0000000..e91f3a4 --- /dev/null +++ b/RSAvatarController.xcodeproj/project.xcworkspace/xcshareddata/RSAvatarController.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + F78C5D8D-FEA8-4FD1-8CDF-628827FD99D7 + IDESourceControlProjectName + RSAvatarController + IDESourceControlProjectOriginsDictionary + + 8E6F39E93010B62143E7BF0514D25B452B518332 + github.com:algal/RSAvatarController.git + + IDESourceControlProjectPath + RSAvatarController.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + 8E6F39E93010B62143E7BF0514D25B452B518332 + ../.. + + IDESourceControlProjectURL + github.com:algal/RSAvatarController.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 8E6F39E93010B62143E7BF0514D25B452B518332 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 8E6F39E93010B62143E7BF0514D25B452B518332 + IDESourceControlWCCName + RSAvatarController + + + + diff --git a/RSAvatarController/DemoViewController.h b/RSAvatarController/DemoViewController.h new file mode 100644 index 0000000..f215842 --- /dev/null +++ b/RSAvatarController/DemoViewController.h @@ -0,0 +1,13 @@ +// +// DemoViewController.h +// RSAvatarController +// +// Created by Alexis Gallagher on 2014-10-03. +// Copyright (c) 2014 Rex Sheng. All rights reserved. +// + +#import + +@interface DemoViewController : UIViewController + +@end diff --git a/RSAvatarController/DemoViewController.m b/RSAvatarController/DemoViewController.m new file mode 100644 index 0000000..9a747ca --- /dev/null +++ b/RSAvatarController/DemoViewController.m @@ -0,0 +1,65 @@ +// +// DemoViewController.m +// RSAvatarController +// +// Created by Alexis Gallagher on 2014-10-03. +// Copyright (c) 2014 Rex Sheng. All rights reserved. +// + +#import "DemoViewController.h" +#import "RSAvatarController.h" + +@interface DemoViewController () +@property (strong,nonatomic) RSAvatarController * rsAvatarController; +@end + +@implementation DemoViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + // add a button that launches the RSAvatarController + UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem]; + [button setTitle:@"LAUNCH RSAVATARCONTROLLER" forState:UIControlStateNormal]; + [button addTarget:self action:@selector(handleTap:) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:button]; + + // center the button + button.translatesAutoresizingMaskIntoConstraints = NO; + NSLayoutConstraint * centerX = [NSLayoutConstraint constraintWithItem:button + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual toItem:self.view + attribute:NSLayoutAttributeCenterX + multiplier:1.0 constant:0]; + NSLayoutConstraint * centerY = [NSLayoutConstraint constraintWithItem:button + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual toItem:self.view + attribute:NSLayoutAttributeCenterY + multiplier:1.0 constant:0]; + [self.view addConstraints:@[centerX,centerY]]; +} + + +- (void) handleTap:(id)sender { + self.rsAvatarController = [[RSAvatarController alloc] init]; + [self.rsAvatarController openActionSheetInController:self]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/RSAvatarController/RSAppDelegate.m b/RSAvatarController/RSAppDelegate.m index 91f4395..bc019b5 100644 --- a/RSAvatarController/RSAppDelegate.m +++ b/RSAvatarController/RSAppDelegate.m @@ -7,14 +7,17 @@ // #import "RSAppDelegate.h" +#import "DemoViewController.h" @implementation RSAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - // Override point for customization after application launch. - self.window.backgroundColor = [UIColor whiteColor]; + + // Override point for customization after application launch. + self.window.rootViewController = [[DemoViewController alloc] init]; + [self.window makeKeyAndVisible]; return YES; } From b37481931d15ccb7dc50112020df0e28da094c58 Mon Sep 17 00:00:00 2001 From: Alexis Gallagher Date: Fri, 3 Oct 2014 09:25:21 -0700 Subject: [PATCH 2/5] add gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bce6af --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +xcuserdata From e2da2994ed52d3f88899a747f34e9f7d1c956e87 Mon Sep 17 00:00:00 2001 From: Alexis Gallagher Date: Fri, 3 Oct 2014 10:53:22 -0700 Subject: [PATCH 3/5] added comments and pragmas --- RSAvatarController.h | 12 +++++++++ RSAvatarController.m | 61 ++++++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/RSAvatarController.h b/RSAvatarController.h index 3dbc880..04557b1 100644 --- a/RSAvatarController.h +++ b/RSAvatarController.h @@ -25,11 +25,23 @@ @protocol RSAvatarControllerDelegate - (void)avatarController:(RSAvatarController *)controller pickedAvatar:(UIImage *)avatar; + +/* Supplies the view that will be overlaid atop the selected image as its being moved and scaled. + This view should include controls (i.e., UIButtons or gesture recognizers) that send "cancel" + and "choose" messages to the trait object, when the user has canceled or chosen their + move and scale transformation on the picked image. + */ - (UIView *)avatarController:(RSAvatarController *)controller overlayForMoveAndScale:(id)trait; + +// Returns rect to be used for popover, on iPad - (CGRect)popoverRectForAvatarController:(RSAvatarController *)controller; + +// size to which selected image will be scaled - (CGSize)destinationImageSizeForAvatarController:(RSAvatarController *)controller; @optional + +// Supplies overlay for the image picker - (UIView *)overlayForAvatarControllerImagePicker:(RSAvatarController *)controller; @end diff --git a/RSAvatarController.m b/RSAvatarController.m index 9d92512..de8e368 100644 --- a/RSAvatarController.m +++ b/RSAvatarController.m @@ -100,49 +100,56 @@ - (void)switchFlashMode } } -- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info +#pragma mark UIPopoverControllerDelegate + +- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { - UIImage *originImage = [info objectForKey:UIImagePickerControllerOriginalImage]; - RSMoveAndScaleController *moveAndScale = [[RSMoveAndScaleController alloc] init]; - moveAndScale.originImage = originImage; - moveAndScale.destinationSize = [self.delegate destinationImageSizeForAvatarController:self]; - moveAndScale.overlayView = [self.delegate avatarController:self overlayForMoveAndScale:moveAndScale]; - moveAndScale.delegate = self; - [picker pushViewController:moveAndScale animated:NO]; + popover = nil; + _imagePicker = nil; } -- (void)moveAndScaleController:(RSMoveAndScaleController *)moveAndScale didFinishCropping:(UIImage *)destImage +#pragma mark UIImagePickerControllerDelegate + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { - [self.delegate avatarController:self pickedAvatar:destImage]; - [self.imagePicker popToRootViewControllerAnimated:NO]; - [self imagePickerControllerDidCancel:self.imagePicker]; + UIImage *originImage = [info objectForKey:UIImagePickerControllerOriginalImage]; + RSMoveAndScaleController *moveAndScale = [[RSMoveAndScaleController alloc] init]; + moveAndScale.originImage = originImage; + moveAndScale.destinationSize = [self.delegate destinationImageSizeForAvatarController:self]; + moveAndScale.overlayView = [self.delegate avatarController:self overlayForMoveAndScale:moveAndScale]; + moveAndScale.delegate = self; + [picker pushViewController:moveAndScale animated:NO]; } -- (void)moveAndScaleControllerDidCancel:(RSMoveAndScaleController *)moveAndScale +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { - [self.imagePicker popToRootViewControllerAnimated:NO]; - [self imagePickerControllerDidCancel:self.imagePicker]; + if (popover) { + [popover dismissPopoverAnimated:YES]; + popover = nil; + } else { + [picker.presentingViewController dismissViewControllerAnimated:YES completion:^{ + _imagePicker = nil; + }]; + } } -- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker +#pragma mark RSMoveAndScaleControllerDelegate + +- (void)moveAndScaleController:(RSMoveAndScaleController *)moveAndScale didFinishCropping:(UIImage *)destImage { - if (popover) { - [popover dismissPopoverAnimated:YES]; - popover = nil; - } else { - [picker.presentingViewController dismissViewControllerAnimated:YES completion:^{ - _imagePicker = nil; - }]; - } + [self.delegate avatarController:self pickedAvatar:destImage]; + [self.imagePicker popToRootViewControllerAnimated:NO]; + [self imagePickerControllerDidCancel:self.imagePicker]; } -- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController +- (void)moveAndScaleControllerDidCancel:(RSMoveAndScaleController *)moveAndScale { - popover = nil; - _imagePicker = nil; + [self.imagePicker popToRootViewControllerAnimated:NO]; + [self imagePickerControllerDidCancel:self.imagePicker]; } #pragma mark - UINavigationControllerDelegate + - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([self.delegate respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) From 987ef1c2c215b96ab76c9dd2ad2b7c913f22a29a Mon Sep 17 00:00:00 2001 From: Alexis Gallagher Date: Fri, 3 Oct 2014 11:33:33 -0700 Subject: [PATCH 4/5] bug fix so that avatar overlay is actually over the scrollable image --- RSMoveAndScaleController.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RSMoveAndScaleController.m b/RSMoveAndScaleController.m index 975debb..94b9395 100644 --- a/RSMoveAndScaleController.m +++ b/RSMoveAndScaleController.m @@ -231,6 +231,9 @@ - (void)viewDidLoad clippingView.clipsToBounds = YES; if (_maximumZoomScale) clippingView.maximumZoomScale = _maximumZoomScale; [self.view addSubview:_clippingView = clippingView]; + + // ensure the overlay is over the image + [self.view bringSubviewToFront:self.overlayView]; } - (void)viewWillAppear:(BOOL)animated From 79e5f4a92b1908d2e04fd0a843f8b59ac2c66321 Mon Sep 17 00:00:00 2001 From: Alexis Gallagher Date: Fri, 3 Oct 2014 11:35:01 -0700 Subject: [PATCH 5/5] demo view controller displays picked,moved, and scaled image --- RSAvatarController/DemoViewController.m | 115 ++++++++++++++++++++---- 1 file changed, 96 insertions(+), 19 deletions(-) diff --git a/RSAvatarController/DemoViewController.m b/RSAvatarController/DemoViewController.m index 9a747ca..3b0296e 100644 --- a/RSAvatarController/DemoViewController.m +++ b/RSAvatarController/DemoViewController.m @@ -11,6 +11,18 @@ @interface DemoViewController () @property (strong,nonatomic) RSAvatarController * rsAvatarController; +@property (weak,nonatomic) UIImageView * pickedImageView; +@end + +@interface DemoViewController (RSAvatarControllerDelegate) +- (void)avatarController:(RSAvatarController *)controller pickedAvatar:(UIImage *)avatar; +- (UIView *)avatarController:(RSAvatarController *)controller overlayForMoveAndScale:(id)trait; +- (CGRect)popoverRectForAvatarController:(RSAvatarController *)controller; +- (CGSize)destinationImageSizeForAvatarController:(RSAvatarController *)controller; + +//@optional +- (UIView *)overlayForAvatarControllerImagePicker:(RSAvatarController *)controller; + @end @implementation DemoViewController @@ -25,25 +37,31 @@ - (void)viewDidLoad { [button setTitle:@"LAUNCH RSAVATARCONTROLLER" forState:UIControlStateNormal]; [button addTarget:self action:@selector(handleTap:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; + + // add a UIImageView beneath it. + UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + [self.view addSubview:imageView]; + self.pickedImageView = imageView; // center the button button.translatesAutoresizingMaskIntoConstraints = NO; - NSLayoutConstraint * centerX = [NSLayoutConstraint constraintWithItem:button - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual toItem:self.view - attribute:NSLayoutAttributeCenterX - multiplier:1.0 constant:0]; - NSLayoutConstraint * centerY = [NSLayoutConstraint constraintWithItem:button - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual toItem:self.view - attribute:NSLayoutAttributeCenterY - multiplier:1.0 constant:0]; - [self.view addConstraints:@[centerX,centerY]]; + imageView.translatesAutoresizingMaskIntoConstraints = NO; + UIView * superview = self.view; + NSDictionary * views = NSDictionaryOfVariableBindings(button,imageView,superview); + [@[@"V:|-(100)-[button]-(20)-[imageView]", // stack the views + @"V:[superview]-(>=0)-[button]"] // center them w/r/t/ superview + enumerateObjectsUsingBlock: + ^(NSString * visualFormat, NSUInteger idx, BOOL *stop) { + [self.view addConstraints: + [NSLayoutConstraint constraintsWithVisualFormat:visualFormat + options:NSLayoutFormatAlignAllCenterX + metrics:nil views:views]]; + }]; } - - (void) handleTap:(id)sender { self.rsAvatarController = [[RSAvatarController alloc] init]; + self.rsAvatarController.delegate = self; [self.rsAvatarController openActionSheetInController:self]; } @@ -52,14 +70,73 @@ - (void)didReceiveMemoryWarning { // Dispose of any resources that can be recreated. } -/* -#pragma mark - Navigation +#pragma mark - RSAvatarControllerDelegate -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. +- (void)avatarController:(RSAvatarController *)controller pickedAvatar:(UIImage *)avatar +{ + NSLog(@"picked image=%@",avatar); + self.pickedImageView.image = avatar; } -*/ +- (UIView *)avatarController:(RSAvatarController *)controller + overlayForMoveAndScale:(id)trait +{ + /* + setup a view holding a "cancel" and "choose" button + */ + UIView * moveAndScaleOverlay = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 100)]; + + UIButton * cancelButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [cancelButton setTitle:@"cancel" forState:UIControlStateNormal]; + + UIButton * chooseButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [chooseButton setTitle:@"choose" forState:UIControlStateNormal]; + + // wire up the buttons to call the trait object + [cancelButton addTarget:trait action:@selector(cancel) forControlEvents:UIControlEventTouchUpInside]; + [chooseButton addTarget:trait action:@selector(choose) forControlEvents:UIControlEventTouchUpInside]; + + + // set up layout so the buttons are left to right with a bit of padding + [moveAndScaleOverlay addSubview:cancelButton]; + [moveAndScaleOverlay addSubview:chooseButton]; + + NSDictionary * views = NSDictionaryOfVariableBindings(cancelButton,chooseButton); + NSMutableArray * constraints = [NSMutableArray array]; + [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[cancelButton]-(>=20)-[chooseButton]-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:views]]; + [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=0)-[cancelButton]-(>=0)-|" options:0 metrics:nil views:views]]; + [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=0)-[chooseButton]-(>=0)-|" options:0 metrics:nil views:views]]; + cancelButton.translatesAutoresizingMaskIntoConstraints = NO; + chooseButton.translatesAutoresizingMaskIntoConstraints = NO; + [moveAndScaleOverlay addConstraints:constraints]; + + CGSize smallerSize = [moveAndScaleOverlay systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; + moveAndScaleOverlay.frame = CGRectMake(moveAndScaleOverlay.frame.origin.x,moveAndScaleOverlay.frame.origin.y, + smallerSize.width,smallerSize.height); + + return moveAndScaleOverlay; +} + +- (CGRect)popoverRectForAvatarController:(RSAvatarController *)controller +{ + // TODO: implement example for iPad + return CGRectMake(0, 0, 100, 100); +} + +- (CGSize)destinationImageSizeForAvatarController:(RSAvatarController *)controller +{ + return CGSizeMake(150, 150); +} + +//@optional +//- (UIView *)overlayForAvatarControllerImagePicker:(RSAvatarController *)controller +//{ +// return nil; +//} + +#pragma mark debugging + +-(void)logTap:(id)sender { + NSLog(@"tap on %@",sender); +} @end