I have a simple application where I can take an photo or choose and existing photo on the camera roll and then display the image in a UIImageView. I want to have a button that you can press and invert the picked image and then display that in the camera roll. My question is, how do invert the colors of a UIImage and then use the new inverted one to display in the ImageView? I have seen other posts related to this but none explain how implement the code. I'm fairly new to xcode and would appreciate all feedback.
Here is my .m file for the view controller:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize imageView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)takePhoto {
picker1 = [[UIImagePickerController alloc] init];
picker1.delegate = self;
[picker1 setSourceType:UIImagePickerControllerSourceTypeCamera];
[self presentViewController:picker1 animated:YES completion:nil];
}
-(IBAction)ChooseExisting {
picker2 = [[UIImagePickerController alloc] init];
picker2.delegate = self;
[picker2 setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
[self presentViewController:picker2 animated:YES completion:nil];
}
-(IBAction)invertImage {
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
imageFinal = [info objectForKey:UIImagePickerControllerOriginalImage];
[imageView setImage:imageFinal];
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
I see the negetiveImage method has a lot of code. Here's a simple one using Core Image filters.
Include Core Image Framework to your project
Import the core Image header.
#import <CoreImage/CoreImage.h>
The below code should return you the inverted UIImage
UIImage *inputImage = [UIImage imageNamed:#"imageName"];
CIFilter* filter = [CIFilter filterWithName:#"CIColorInvert"];
[filter setDefaults];
[filter setValue:inputImage.CIImage forKey:#"inputImage"];
UIImage *outputImage = [[UIImage alloc] initWithCIImage:filter.outputImage];
The outputImage will be inverted
To give you an easy answer, you can invert the UIImageView which has your UIImage.
To invert it, just use
[yourImageView setTransform:CGAffineTransformMakeScale(-1, 1)]; // flip horizontally
[yourImageView setTransform:CGAffineTransformMakeScale(1, -1)]; // flip vertically
[yourImageView setTransform:CGAffineTransformMakeScale(-1, -1)]; // flip horizontally and vertically
and to bring it back to normal, use
[yourImageView setTransform:CGAffineTransformIdentity]; // bring the view back to normal.
Hope this helps and I wish you all the best with you app :)
Related
How can I assure that UIImagePickerController always returns a squared image?
WhatsApp does it but I have not found a way to achieve this.
How it is in my App (Build in iOS 7)
How it should be (WhatsApp - iOS 6)
My code:
- (IBAction)showImagePicker:(id)sender {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.allowsEditing = YES;
if ([sender tag] == 1) {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
else {
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
[self presentModalViewController:imagePicker animated:YES];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
_imageView.image = [info objectForKey:UIImagePickerControllerEditedImage];
[self dismissModalViewControllerAnimated:YES];
}
well to change the size of the image captured you need to set the mode for view in your UIImageView in IB to scale to fill. then when the image is chosen from library or from camera, it automaticaly will be resized. you do have to turn the autolayout off though since it wont allow the ImageView to be resized. you code is fine. anyhow here is sample app codes you can build to understand it a bit better.
.h
#import <UIKit/UIKit.h>
#interface ImageTestViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
#property (strong, nonatomic) IBOutlet UIImageView *imageView;
- (IBAction)takePhoto: (UIButton *)sender;
- (IBAction)selectPhoto:(UIButton *)sender;
#end
.m
#import "ImageTestController.h"
#interface ImageTestViewController ()
#end
#implementation ImageTestViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Device has no camera"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[myAlertView show];
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (IBAction)takePhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker animated:YES completion:NULL];
}
- (IBAction)selectPhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
self.imageView.image = chosenImage;
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:NULL];
}
#end
in the ImageTestController Xib or view in storyboard, place two buttons, one for each action and create the necessary links in IB. then place a UIImageView to fill whatever size you want in the view. click the UIImageView and in view section of attribute inspector change the mode to scale to fill. turn the autolayout off. try adding an image to your simulator just to test it. make sure the image is of the landscape size. build and run and when you select the image from image library, it will automatically resize to square or any other size you set your UIImageView.
i know you are aware of most of the steps above, but i wrote this answer not to just help you get rid of your issue but for others that may have similar issues. hope it helps you out man.
I am trying to make my image update once a new on is selected.
The update works but it does not show up right away when the new image is selected during editing and I was wondering if anyone has any idea how to fix it.
The app was originally created for iPhone and this function works fine, the image is updated as soon as a new one is selected even during editing for the iPhone, but when I use it on the iPad the image does not change during editing but once I finish the editing and go back, I can see the image is change.
I am making the core data recipe sample code from the developer center to work on the iPad.
This is my code for selecting a new Image PhotoTapped1 button, this has been change to work with the iPad:
- (IBAction)photoTapped1 {
if(UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone){
// If in editing state, then display an image picker; if not, create and push a photo view controller.
if (self.editing) {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:nil];
[imagePicker release];
} else {
RecipePhotoViewController *recipePhotoViewController = [[RecipePhotoViewController alloc] init];
recipePhotoViewController.hidesBottomBarWhenPushed = YES;
recipePhotoViewController.recipe = recipe;
[self.navigationController pushViewController:recipePhotoViewController animated:YES];
[recipePhotoViewController release];
}
}else{
if (self.editing){
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
self.popover = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
[self.popover presentPopoverFromRect:CGRectMake(0, 0, 400, 400) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
self.popover.delegate = self;
[popover release];
}else{
RecipePhotoViewController *recipePhotoViewController = [[RecipePhotoViewController alloc] init];
recipePhotoViewController.hidesBottomBarWhenPushed = YES;
recipePhotoViewController.recipe = recipe;
[self.navigationController pushViewController:recipePhotoViewController animated:YES];
[recipePhotoViewController release];
}}}
This is working fine and the image is update on, but not during editing.
During editing it shows the image that was there all along, so it can be confusing as anyone using the app can not be sure if the new selected image has been added or updated.
The following is the imagePickerController and the updateButton code:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)selectedImage editingInfo:(NSDictionary *)editingInfo {
// Delete any existing image.
NSManagedObject *oldImage = recipe.image;
if (oldImage != nil) {
[recipe.managedObjectContext deleteObject:oldImage];
}
// Create an image object for the new image.
NSManagedObject *image = [NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:recipe.managedObjectContext];
recipe.image = image;
// Set the image for the image managed object.
[image setValue:selectedImage forKey:#"image"];
// Create a thumbnail version of the image for the recipe object.
CGSize size = selectedImage.size;
CGFloat ratio = 0;
if (size.width > size.height) {
ratio = 44.0 / size.width;
} else {
ratio = 44.0 / size.height;
}
CGRect rect = CGRectMake(0.0, 0.0, ratio * size.width, ratio * size.height);
UIGraphicsBeginImageContext(rect.size);
[selectedImage drawInRect:rect];
recipe.thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self dismissModalViewControllerAnimated:YES];}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion: nil];
}
Here is the updatePhotoButton:
- (void)updatePhotoButton {
/* How to present the photo button depends on the editing state and whether the recipe has a thumbnail image.
* If the recipe has a thumbnail, set the button's highlighted state to the same as the editing state (it's highlighted if editing).
* If the recipe doesn't have a thumbnail, then: if editing, enable the button and show an image that says "Choose Photo" or similar; if not editing then disable the button and show nothing.
*/
BOOL editing = self.editing;
if (recipe.thumbnailImage != nil) {
photoButton.highlighted = editing;
} else {
photoButton.enabled = editing;
if (editing) {
[photoButton setImage:[UIImage imageNamed:#"choosePhoto.png"] forState:UIControlStateNormal];
} else {
[photoButton setImage:nil forState:UIControlStateNormal];
}
}
}
Does anyone has any idea what would be the problem? I know the UIImagePickerControll has to be used with UIPopoverController in order to work on the iPad, but that is being updated on the -(IBAction)PhotoTapped1 code.
Not sure where to go from here.
Thank you.
Update to question..
PhotoViewController.h
#class Recipe;
#interface RecipePhotoViewController : UIViewController {
#private
Recipe *recipe;
UIImageView *imageView;
}
#property(nonatomic, retain) Recipe *recipe;
#property(nonatomic, retain) UIImageView *imageView;
#end
RecipePhotoViewController.m
#import "RecipePhotoViewController.h"
#import "Recipe.h"
#implementation RecipePhotoViewController
#synthesize recipe;
#synthesize imageView;
- (void)loadView {
self.title = #"Photo";
imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.backgroundColor = [UIColor blackColor];
self.view = imageView;
}
- (void)viewWillAppear:(BOOL)animated {
imageView.image = [recipe.image valueForKey:#"image"];
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)dealloc {
[imageView release];
[recipe release];
[super dealloc];
}
#end
This is an updated data to original question
The reason there's no problem on iPhone is that the previous image isn't visible on iPhone, because you're using a presented view controller: the image picker takes over the entire screen. On iPad, on the other hand, you're using a popover, so the main interface is still visible behind the popover. Thus the user is able to see that the image being tapped is not the image that appears back in the main interface.
But might argue that there's no problem with this. The user hasn't actually accepted the image yet, until the image picker popover is dismissed. You say:
During editing it shows the image that was there all along, so it can be confusing as anyone using the app can not be sure if the new selected image has been added or updated.
But what if the user taps outside the popover to dismiss / cancel it? Surely you wouldn't want the image to have changed in the main interface. The behavior you're objecting to, therefore, is right: you're complaining that the image in the main interface is not updated until the popover is actually dismissed, and that's exactly how it should be. When the user is working in a popover, what's going on behind the popover is irrelevant.
I'm trying to display a UIImage selected from a UIImagePickerController inside a subclass of CALayer and add it to my superview but I can't get it to work.
This is how I Get the Image from the UIImagePickerController, which seems to work good :
- (IBAction)importImage:(id)sender
{
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.delegate = self;
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:imagePickerController];
[popover setPopoverContentSize:CGSizeMake(320,320)];
[popover presentPopoverFromRect:importBackgroundButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
self.popoverController = popover;
[imagePickerController release];
}
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self.popoverController dismissPopoverAnimated:YES];
UIImage* img = [info objectForKey:UIImagePickerControllerOriginalImage];
[[context currentDrawing] setBkgImage:img];
}
This is my subclass of CALayer which contains the UIImage to display :
#interface UI_BackgroundImageLayer : CALayer
{
//This is for the Import image as background feature
UIImage* _bkgImg;
}
#property (nonatomic, retain) UIImage* _bkgImg;
-(void)update:(UIImage*)src;
#end
////
#implementation UI_BackgroundImageLayer
- (id)init
{
self = [ super init ];
// [self resetTransform ];
_bkgImg = nil;
return self;
}
-(void)update:(UIImage*)src
{
_bkgImg = [[UIImage alloc] initWithCGImage:src.CGImage];
self.contentsGravity = kCAGravityResizeAspect;
self.contents = (id)_bkgImg.CGImage;
}
#end
I update the content of the CALayer when the user select another image !
The CALayer is added to the superview from start like this :
[[_superView layer] insertSublayer:context._imgLayer atIndex:0];
It's inserted at index 0 because it's meant to be a background image so it must be under other UI elements and drawings.
All of this seems good to me, but nothing appears on the screen ...
Are you using XCode v4.2? If so, this may be relevant to you or it may not but it's worth a look. I've had exacty the same problem as you with CALayer subclasses not responding to the usual display methods (like setCornerRadius, setBackgroundColor etc...). Try this; in your hosting view, just after you've instanced the VIEW put the following line;
[self setBackgroundColor [UIColor clearColor]];
You may well find that from that point on, your layer subclass works perfectly.
My view subclass code looks like this;
// layerClass
+ (Class) layerClass {
return [myLayerSubclass class];
}
// View subclass init routine
- (id) initWithFrame: (CGRect) p_frame {
self = [super initWithFrame: p_frame];
if (self) {
[self setBackgroundColor: [UIColor clearColor]]; // *** BUG WORKARUND? *** Note: Do Apple know about this?
// Rest of your view init ....
}
return self;
}
It seems that the layer subclass does not respond until its hosting view has 'acknowledged' that it's there. It took me days to debug but now all my code works perfectly.
Hope this helps.
V.V.
I have a problem with my application. It crashes after few times of working with images and I have no idea why.
I have a TabBar, in one item of TabBat I have a navigation controller. In the root controller of that navigation I have a two buttons. One button is to get an image from camera or photo library, the second button is to get an image from saved images in the app. With selected image I go to next view controller (SelectImageViewController).
With the first button I use that code:
- (void) showImagePickerWithSourceType:(NSInteger) sourceType
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = sourceType;
picker.allowsEditing = NO;
[self presentModalViewController:picker animated:YES];
[picker release];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *selectedImage = (UIImage *)[info objectForKey:#"UIImagePickerControllerEditedImage"];
if (selectedImage == nil)
selectedImage = (UIImage *)[info objectForKey:#"UIImagePickerControllerOriginalImage"];
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
SelectImageViewController *selectImageViewController = [[[SelectImageViewController alloc] initWithImage:selectedImage] autorelease];
[self.navigationController pushViewController:selectImageViewController animated:YES];
}
With the second button I use:
DocumentsViewController *documentsViewController = [[[DocumentsViewController alloc] init] autorelease];
[self.navigationController pushViewController:documentsViewController animated:YES];
DocumentsViewController is a TableViewController with list of images, when I select one image than I go to the SelectImageViewController.
In SelectImageViewController I make some image processing and then go to next view controller where I make another image processing.
Here is some code from SelectImageViewController:
- (id) initWithImage:(UIImage *) image
{
self = [super init];
if (!self) return nil;
self.originalImage = image;
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
[NSThread detachNewThreadSelector:#selector(processImage) toTarget:self withObject:nil];
}
- (void) processImage
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
self.originalImage = [ImageHelper scaleAndRotateImage:self.originalImage toSize:CGSizeMake(1500, 1500)];
self.processedImage = [ImageHelper scaleImage:self.originalImage maxWidth:320 maxHeight:367];
[self performSelectorOnMainThread:#selector(finish) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void) finish
{
imageView = [[UIImageView alloc] initWithImage:self.processedImage];
imageView.center = self.view.center;
[self.view addSubview:imageView];
[self.view sendSubviewToBack:imageView];
[NSThread detachNewThreadSelector:#selector(processImage2) toTarget:self withObject:nil];
}
- (void) processImage2
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *corners = [OpenCV processImage:self.processedImage];
[self performSelectorOnMainThread:#selector(finish2) withObject:nil waitUntilDone:NO];
[pool release];
}
In the SelectImageViewController I have one button and when I click it I process the original image and then go to the next view controller.
In the last view controller in viewDidLoad method I process one more time the image.
I know that all method for image processing take a lot of memory and some time (especially that my original image which I use often is not scaled), but the application also crashes when I only select an image from photo library and go to SelectImageViewController. Maybe I shouldn't use a navigation controller? Has anybody an idea what can be wrong?
Thanks for any help.
Take care about the autorelease. Functions usually return autorelease objects. You are within an autorelease pool so, it is possible that the image wont exists when you call the last selector. Try assigning the images by using de setter method
[self setProcessedImage:[ImageHelper scaleImage:self.originalImage maxWidth:320 maxHeight:367]];
only if you have declared your variable as an instance property with retain or copy, it will retain the image.
Use NSZombieEnabled to see what happens. It seems to be a classic problem of a freed object.
This is my code so far:
/* class: myViewController
#interface myViewController: UIViewController
<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
*/
- (IBAction) getPicture {
UIImagePickerController * picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:picker animated:YES];
}
- (void) imagePickerController:(UIImagePickerController *)thePicker
didFinishPickingMediaWithInfo:(NSDictionary *)imageInfo
{
[[thePicker parentViewController] dismissModalViewControllerAnimated:YES];
UIImage *img = [imageInfo
objectForKey:#"UIImagePickerControllerOriginalImage"];
self.myImageView.image = img;
}
So basically I'm trying to get a photo from the iPhone camera and display it in a UIImageView. This works perfectly fine as long the class myViewController is displayed as a standalone view. If I'm putting the View inside a UINavigationController the UIImageView won't display the image after taking one with the camera. But if I choose a picture from the library everything is fine again.
So why does the UIImageView won't display a image taken with the camera inside a UINavigationController?
Try to switch the following lines:
[thePicker dismissModalViewControllerAnimated:YES];
UIImage *img = [imageInfo objectForKey:#"UIImagePickerControllerOriginalImage"];
self.myImageView.image = img;
to the following:
UIImage *img = [imageInfo objectForKey:#"UIImagePickerControllerOriginalImage"];
self.myImageView.image = img;
[thePicker dismissModalViewControllerAnimated:YES];
Perhaps the image is released because of the dismissing of the view before you retain it with
self.myImageView.image = img;
Seems to me you're dismissing the wrong viewcontroller. Shouldn't:
[thePicker dismissModalViewControllerAnimated:YES];
read:
[self dismissModalViewControllerAnimated:YES];