Issue with VPImageCropperViewController API - ios

I am going to share my image to Instagram , but before sharing in need to user crop their own photo , so I used VPImageCropperViewController (https://github.com/windshg/VPImageCropper) to crop the image first then share it to Instagram but the result is over scaled image :
Crop area :
and the result :
here is my codes :
- (IBAction)shareIt:(id)sender {
UIGraphicsBeginImageContextWithOptions(captureView.bounds.size, NO, 0.0);
[captureView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
VPImageCropperViewController *imgCropperVC =
[[VPImageCropperViewController alloc]
initWithImage:image
cropFrame:CGRectMake(0, 100.0f, self.view.frame.size.width,self.view.frame.size.width)
limitScaleRatio:3.0];
imgCropperVC.delegate = self;
[self presentViewController:imgCropperVC animated:YES completion:nil];
}
VPImageCropperDelegate
- (void)imageCropper:(VPImageCropperViewController *)cropperViewController didFinished:(UIImage *)editedImage {
[cropperViewController dismissViewControllerAnimated:YES completion:^{
NSURL *instagramURL = [NSURL URLWithString:#"instagram://location?id=1"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
NSURL *url;
docFile.delegate = self;
//Save image to directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:#"savedImage.jpg"];
NSData *imageData = UIImagePNGRepresentation(editedImage);
[imageData writeToFile:savedImagePath atomically:NO];
//load image
NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:#"savedImage.jpg"];
UIImage *tempImage = [UIImage imageWithContentsOfFile:getImagePath];
//Hook it with Instagram
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Image.ig"];
[UIImageJPEGRepresentation(tempImage, 1.0) writeToFile:jpgPath atomically:YES];
url = [[NSURL alloc] initFileURLWithPath:jpgPath];
docFile = [UIDocumentInteractionController interactionControllerWithURL:url];
[docFile setUTI:#"com.instagram.photo"];
docFile.annotation = #{#"InstagramCaption" : #" #geometrica" };
[docFile presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
}else {
UIAlertView *errorToShare = [[UIAlertView alloc] initWithTitle:#"Instagram unavailable " message:#"You need to install Instagram" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorToShare show];
}
}];
}

It seems like the error here lies in the instatiation of your imgCropperVC. You are telling the cropper to crop out a specific rectangle (with your yellow border's size) from the image you are providing. Since you said in a comment here that the size of the picture is the same size as the screen, and since your result seems to be EXACTLY half of the width (it's cropped directly through his face), I sugges trying
[[VPImageCropperViewController alloc]
initWithImage:image
cropFrame:CGRectMake(0, 100.0f, self.view.frame.size.width*2,self.view.frame.size.width*2)
limitScaleRatio:3.0];
This might have something to do with #2x, though I'm not really sure this solves your issue for other images and in general.
I suspect the original picture to be bigger than the screen, and scaled down to fit it. If this is correct, weird things(not really) happen.
Your screen's size is probably 640px wide. Let's assume the original picture(picture.jpg) has a width of 1280 pixels or something. When showing this in your UIImageView in your ViewController or UIScrollView, it will obviously scale to fit, or else you wouldn't be able to see the entire picture. If you now use your line of code, then you are asking the cropper to crop out a width of 640 (the width of your own screen and rectangle) from a picture that is 1280px wide. The cropper doesn't care how wide your screen is, it just crops out 640px from a 1280px wide image because that's what you told it to do. This will result in the image being cut in half. If your image is scaled down (which it is by default, I guess), you'll need to use this scale in your line of code. If the original picture actually IS 1280px wide, then my above code will work, because I double the rectangle's size (The width of your view is probably 320, this is the same as 640 retina I believe).
If you multiply self.view.fram.size.width and height by the amount the picture has been zoomed, you should get the correct image. This also applies to the "static" 100.0f you send. For your VIEW it's 100f, but if the image is scaled up or down, this will not be correct. You need to multiply this with the same value as well. I'm sure you can get the zoom-scale from the scrollView or imageView or something.
I suggest testing your app with different image sizes. If an image with the exact same pixel-size as your UIView is tested, I think it will work perfectly, and will confirm my suspicion.

Finally I figure it out to crop image size there is a method in project demo called :
- (UIImage *)imageByScalingAndCroppingForSourceImage:(UIImage *)sourceImage targetSize:(CGSize)targetSize;
you can mention your target images size with it some thing like this :
image = [self imageByScalingAndCroppingForSourceImage:image targetSize:CGSizeMake(1024, 1024)];

Related

Best way to manage image orientation?

I am trying to set different images on a UIImageView but I don´t know how to handle the image orientation because always that I call image.imageOrientation the result is UIImageOrientationUp and it isn´t.
Here some examples:
Image with wrong orientation.
Image with wrong orientation.
I want that all images look correctly but I don´t know how to do it if the image.imageOrientation don´t give me the correct value.
Here my code:
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:#"porfileImage.png"];
UIImage *img = [UIImage imageWithContentsOfFile:getImagePath];
if (img == nil) {
img = [UIImage imageNamed:#"user"];
}
NSLog(#"Orien: %ld",(long)img.imageOrientation);
self.porfileImage.image = [UIImage imageWithCGImage:[img CGImage] scale:[img scale] orientation: UIImageOrientationUp];
I found my error!!
I was saving the image using UIImagePNGRepresentation(image); so the image lose the orientation and when I wanted to use it, the image already was a default orientation that isn´t the correct one.
The solution is to use UIImageJPEGRepresentation(image, 1.0); instead of UIImagePNGRepresentation(image); and for automatically the image save with the correct orientation.
Nearly all the images clicked by a camera have metadata/ EXIF data which has the image orientation value. As the image is transmitted or passed along various channels like email, messaging etc. some of this data might be stripped off. So every time you load the image using CGImage, it takes the default orientation of UIImageOrientationUp.
One solution might be to create two versions of your image, UIImage and CGImage, and compare their height. If they are equal, then the CGImage version is not be rotated and you are good. Else, the CGImage conversion has messed up the orientation. In that case, swap the x/y coordinate of origin and try to rotate it.

Image cropping from UIImageView

I have a UIImageView with size (0,0,414,471). The image with size (0,0,1236,1242) is displayed with UIViewContentModeScaleAspectFit mode in the UIImageView. So the image is nicely displayed. Then I make a rectangle (0,0,100,100) on the image and I like to crop it. Even though the rectangle covered (100,100) on the UIImageView, on the actual image size, (100,100) does not cover the size as shown on the UIImageView. So I make xratio (actual width/display width) and yratio(actual height/display height), then I multiply the rectangle size, to all x,y,width and height of the rectangle. This approach looks like I can crop the area as close as possible as shown on the UIImageView. But still have a bit of area offset and distortion. What could be the best way to crop in this scenario?
My code is shown below
- (void)saveCroppedImage:(UIImage *)image withcroppedRectangle:(CGRect)clippedRect
{
float xratio = image.size.width/displayWidth;
float yratio = image.size.height/displayHeight;
// Crop logic
clippedRect.origin.x *= xratio;
clippedRect.origin.y *= yratio;
clippedRect.size.width *= xratio;
clippedRect.size.height *= yratio;
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
UIImage * croppedImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
NSString *imageName =#"Documents/";
imageName = [imageName stringByAppendingString:[CurrentToddlerProfile getUserID]];
imageName = [imageName stringByAppendingString:#".png"];
NSString *pngPath = [NSHomeDirectory() stringByAppendingPathComponent:imageName];
// Write image to PNG
[UIImagePNGRepresentation(croppedImage) writeToFile:pngPath atomically:YES];
// Create file manager
// NSFileManager *fileMgr = [NSFileManager defaultManager];
// NSError *error;
// // Point to Document directory
// NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
// // Write out the contents of home directory to console
// NSLog(#"Documents directory: %#", [fileMgr contentsOfDirectoryAtPath:documentsDirectory error:&error]);
}
Are you sure you need two ratios ? If you want your image to have the right proportions, you should use only one ratio.
If you need to fill a rectangle ( Even if UIImageView does it for you ), then first compute the ratio on x axis. Compute the scaled height using this ratio, if it is smaller than the height you need to cover, then compute the ratio on y axis.
Finally, the ratio just computed is the one to use to scale your image.

UIImageView ignores content mode form image data

I am trying load an image from a data :
NSError *error ;
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:#"png"];
NSData *encryptedData = [NSData dataWithContentsOfFile:imagePath];
NSData *decryptedData = [RNDecryptor decryptData:encryptedData
withPassword:PASSWORD
error:&error];
UIImage *image = [UIImage imageWithData:decryptedData];
//adding image
UIImageView *movingImageView = [[UIImageView alloc]initWithImage:image];
[movingImageView setContentMode:UIViewContentModeScaleAspectFit];
[self.scrollView addSubview:movingImageView];
now movingImageView ignores content mode !!! it will be fixed if I declare its frame ! but the problem is my datas are different images with different width size . Any solution ?
EDIT :
Found a solution :
movingImageView.frame = CGRectMake(0, 0, image.size.width/2, image.size.height/2);
According to the documentation:
initWithImage: adjusts the frame of the receiver to match the size of the specified image. It also disables user interactions for the image view by default.`
Obviously the contentMode will have no effect if the imageView and image have the same size.
I guess what you need is to specify the imageView's width (probably the same as scrollView?), and calculate the height according to the image's dimension.
use the following lines. it will fill the image to frame. if large image came it will show image with aspect ratio and some part may clips. try once.
movingImageView.contentMode = UIViewContentModeScaleAspectFill;
movingImageView.clipsToBounds = YES;[self.scrollView addSubview:movingImageView];

Screen shot not provide image of whole screen

I am making application related to images. I have multiple images on my screen. I had take screen shot of that. But it should not provide my whole screen.
Little part of the top most & bottom most part need not be shown in that.
I have navigation bar on top. And some buttons at bottom. I don't want to capture that buttons and navigation bar in my screenshot image.
Below is my code for screen shot.
-(UIImage *) screenshot
{
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, [UIScreen mainScreen].scale);
[self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:YES];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
After taking screenshot I am using it by below code in facebook share method,
UIImage *image12 =[self screenshot];
[mySLComposerSheet addImage:image12];
the easiest way to achieve this would be to add a UIView which holds all the content you want to take a screenshot of and then call drawViewHierarchyInRect from that UIView instead of the main UIView.
Something like this:
-(UIImage *) screenshot {
UIGraphicsBeginImageContextWithOptions(contentView.bounds.size, YES, [UIScreen mainScreen].scale);
[contentView drawViewHierarchyInRect:contentView.frame afterScreenUpdates:YES];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
Hope this helps!
You can use my below code to take screen shot of a view.
I have put the condition to check the size of a screenshot.
With this code image is saved in your documents folder and from there you can use your image to share on Facebook or anywhere you want to share.
CGSize size = self.view.bounds.size;
CGRect cropRect;
if ([self isPad])
{
cropRect = CGRectMake(110 , 70 , 300 , 300);
}
else
{
if (IS_IPHONE_5)
{
cropRect = CGRectMake(55 , 25 , 173 , 152);
}
else
{
cropRect = CGRectMake(30 , 25 , 164 , 141);
}
}
/* Get the entire on screen map as Image */
UIGraphicsBeginImageContext(size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * mapImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
/* Crop the desired region */
CGImageRef imageRef = CGImageCreateWithImageInRect(mapImage.CGImage, cropRect);
UIImage * cropImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
/* Save the cropped image
UIImageWriteToSavedPhotosAlbum(cropImage, nil, nil, nil);*/
//save to document folder
NSData * imageData = UIImageJPEGRepresentation(cropImage, 1.0);
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString *imagename=[NSString stringWithFormat:#"Pic.jpg"];
NSString* fullPathToFile = [documentsDirectory stringByAppendingPathComponent:imagename];
////NSLog(#"full path %#",fullPathToFile);
[imageData writeToFile:fullPathToFile atomically:NO];
Hope it helps you.
use this code
-(IBAction)captureScreen:(id)sender
{
UIGraphicsBeginImageContext(webview.frame.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
}
sample project www.cocoalibrary.blogspot.com
http://www.bobmccune.com/2011/09/08/screen-capture-in-ios-apps/
snapshotViewAfterScreenUpdates
but it is only Available in iOS 7.0 and later.
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
This method captures the current visual contents of the screen from the render server and uses them to build a new snapshot view. You can use the returned snapshot view as a visual stand-in for the screen’s contents in your app. For example, you might use a snapshot view to facilitate a full screen animation. Because the content is captured from the already rendered content, this method reflects the current visual appearance of the screen and is not updated to reflect animations that are scheduled or in progress. However, this method is faster than trying to render the contents of the screen into a bitmap image yourself.
https://developer.apple.com/library/ios/documentation/uikit/reference/UIScreen_Class/Reference/UIScreen.html#//apple_ref/occ/instm/UIScreen/snapshotViewAfterScreenUpdates:

Objective C / Drawing and resolution questions

I need to create a calendar of an image (they are size 640 x 960 or 960 x 640). Now one approach I tried was to create a view, add an image view to it, present the image, "draw" on it with subviews, then save to view to a file.
Now this works as planned, but testing it in simulator, the resolution of the saved image is 306 x 462 (size of the view I'm saving). Now I just lost half my original resolution...
Is there any way to work around this?
Code that saved the view:
UIGraphicsBeginImageContext(self.backgroundImageView.bounds.size);
[self.backgroundImageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *fullPath = [NSString stringWithFormat:#"%#/Calendar.png",basePath];
NSData * binaryImageData = UIImagePNGRepresentation(image);
[binaryImageData writeToFile:fullPath atomically:YES];
You're not accounting for device screen scale, that should do it: UIGraphicsBeginImageContextWithOptions(self.backgroundImageView.bounds.size, YES, [UIScreen mainScreen].scale);.
If anyone has similar problem, I've come up with 2 solutions:
1) Enlarge everything before saving (you'll have to play around with AutoresizingMasks).
2) Put everything into a scrollView and display it in the large size so the user scrolls around if he wants to see everything.

Resources