UIImageView pinch zoom and reposition to fit overlay - ios

I am using an UIImagePickerView to grab photos from the camera or camera roll. When the user 'picks' an image, I'm inserting the image onto an UIImageView, which is nested in a UIScrollView to allow pinch/pan. I have an overlay above the image view which represents the area to which the image will be cropped (just like when UIImagePickerView's .allowEditing property is YES).
The Apple-provided "allowEditing" capability also has the same problem I'm seeing with my code (which I why I tried to write it myself in the first place, and I need custom shapes in the overlay). The problem is that I can't seem to find a good way to allow the user to pan around over ALL the image. There are always portions of the image which can't be placed in the crop window. It's always content around the edges (maybe the outside 10%) of the image, which cannot be panned into the crop window.
In the above photo, the brown area at the top and bottom are the scroll view's background color. The image view is sized to the image.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// Calculate what height/width we'll need for our UIImageView.
CGSize screenSize = [UIScreen mainScreen].bounds.size;
float width = 0.0f, height = 0.0f;
if (image.size.width > image.size.height) {
width = screenSize.width;
height = image.size.height / (image.size.width/screenSize.width);
} else {
height = screenSize.height;
width = image.size.width / (image.size.height/screenSize.height);
}
if (width > screenSize.width) {
height /= (width/screenSize.width);
width = screenSize.width;
}
if (height > screenSize.height) {
width /= (height/screenSize.height);
height = screenSize.height;
}
// Update the image view to the size of the image and center it.
// Image view is a subview of the scroll view.
imageView.frame = CGRectMake((screenSize.width - width) / 2, (screenSize.height - height) / 2, width, height);
imageView.image = image;
// Setup our scrollview so we can scroll and pinch zoom the image!
imageScrollView.contentSize = CGSizeMake(screenSize.width, screenSize.height);
// Close the picker.
[[picker presentingViewController] dismissViewControllerAnimated:YES completion:NULL];
}
I've considered monitoring scroll position and zoom level of the scroll view and disallow a side of the image to pass into the crop "sweet spot" of the image. This seems like over-engineering, however.
Does anyone know of a way to accomplish this?

I'm a moron. What a difference a good night's sleep can make ;-) Hopefully, it will help someone in the future.
Setting the correct scroll view contentSize and contentInset did the trick. The working code is below.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// Change the frame of the image view so that it fits the image!
CGSize screenSize = [UIScreen mainScreen].bounds.size;
float width = 0.0f, height = 0.0f;
if (image.size.width > image.size.height) {
width = screenSize.width;
height = image.size.height / (image.size.width/screenSize.width);
} else {
height = screenSize.height;
width = image.size.width / (image.size.height/screenSize.height);
}
// Make sure the new height and width aren't bigger than the screen
if (width > screenSize.width) {
height /= (width/screenSize.width);
width = screenSize.width;
}
if (height > screenSize.height) {
width /= (height/screenSize.height);
height = screenSize.height;
}
CGRect overlayRect = cropOverlay.windowRect;
imageView.frame = CGRectMake((screenSize.width - width) / 2, (screenSize.height - height) / 2, width, height);
imageView.image = image;
// Setup our scrollview so we can scroll and pinch zoom the image!
imageScrollView.contentSize = imageView.frame.size;
imageScrollView.contentInset = UIEdgeInsetsMake(overlayRect.origin.y - imageView.frame.origin.y,
overlayRect.origin.x,
overlayRect.origin.y + imageView.frame.origin.y,
screenSize.width - (overlayRect.origin.x + overlayRect.size.width));
// Dismiss the camera's VC
[[picker presentingViewController] dismissViewControllerAnimated:YES completion:NULL];
}
The scrollview and image view are set up like this:
imageScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
imageScrollView.showsHorizontalScrollIndicator = NO;
imageScrollView.showsVerticalScrollIndicator = NO;
imageScrollView.backgroundColor = [UIColor blackColor];
imageScrollView.userInteractionEnabled = YES;
imageScrollView.delegate = self;
imageScrollView.minimumZoomScale = MINIMUM_SCALE;
imageScrollView.maximumZoomScale = MAXIMUM_SCALE;
[self.view addSubview:imageScrollView];
imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.backgroundColor = [UIColor grayColor]; // I had this set to gray so I could see if/when it didn't align properly in the scroll view. You'll likely want to change it to black
[imageScrollView addSubview:imageView];
Edit 3-21-14 Newer, fancier, better implementation of the method that calculates where to place the image in the screen and scrollview. So what's better? This new implementation will check for any image that is being set into the scrollview which is SMALLER in width or height, and adjust the frame of the image view such that it expands to be at least as wide or tall as the overlay rect, so you don't ever have to worry about your user selecting an image that isn't optimal for your overlay. Yay!
- (void)imagePickerController:(UIImagePickerController *)pickerUsed didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// Change the frame of the image view so that it fits the image!
CGSize screenSize = [UIScreen mainScreen].bounds.size;
float width = 0.0f, height = 0.0f;
if (image.size.width > image.size.height) {
width = screenSize.width;
height = image.size.height / (image.size.width/screenSize.width);
} else {
height = screenSize.height;
width = image.size.width / (image.size.height/screenSize.height);
}
CGRect overlayRect = cropOverlay.windowRect;
// We should check the the width and height are at least as big as our overlay window
if (width < overlayRect.size.width) {
float ratio = overlayRect.size.width / width;
width *= ratio;
height *= ratio;
}
if (height < overlayRect.size.height) {
float ratio = overlayRect.size.height / height;
height *= ratio;
width *= ratio;
}
CGRect imageViewFrame = CGRectMake((screenSize.width - width) / 2, (screenSize.height - height) / 2, width, height);
imageView.frame = imageViewFrame;
imageView.image = image;
// Setup our scrollview so we can scroll and pinch zoom the image!
imageScrollView.contentSize = imageView.frame.size;
imageScrollView.contentInset = UIEdgeInsetsMake(overlayRect.origin.y - imageView.frame.origin.y,
(imageViewFrame.origin.x * -1) + overlayRect.origin.x,
overlayRect.origin.y + imageView.frame.origin.y,
imageViewFrame.origin.x + (screenSize.width - (overlayRect.origin.x + overlayRect.size.width)));
// Calculate the REAL minimum zoom scale!
float minZoomScale = 1 - MIN(fabsf(fabsf(imageView.frame.size.width) - fabsf(overlayRect.size.width)) / imageView.frame.size.width,
fabsf(fabsf(imageView.frame.size.height) - fabsf(overlayRect.size.height)) / imageView.frame.size.height);
imageScrollView.minimumZoomScale = minZoomScale;
// Dismiss the camera's VC
[[picker presentingViewController] dismissViewControllerAnimated:YES completion:NULL];
}

Related

Image resizing inside tableviewcell

I ran into a problem that can not solve 2 days. On the server image and come before inserting them in the box, I cut them on the client to fit the screen. Everything is good but the images change randomly height. Now the height is calculated independently - in proportion to the width.
Normal image:
Bad image
I can not ask explicitly UIImageView because I dynamically I rely all the cells to different devices.
My resize function:
-(UIImage *)resizeImage :(UIImage *)theImage :(CGSize)theNewSize {
UIGraphicsBeginImageContextWithOptions(theNewSize, NO, 1.0);
CGFloat height = theImage.size.height;
CGFloat newHeight = 0;
newHeight = (theNewSize.width * height) / theImage.size.width;
newHeight = floorf(newHeight);
NSLog(#"new height image %f", newHeight);
[theImage drawInRect:CGRectMake(0, 0, theNewSize.width, newHeight)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
inside layoutSubviews:
if(device == thisDeviceClass_iPhone5) {
[self.imageView setFrame:CGRectMake(0,0, 320, 255)]; //180
offset = 0;
padding = 5;
} else if(device == thisDeviceClass_iPhone6){
[self.imageView setFrame:CGRectMake(0,0, 375, 255)]; //211
offset = 25;
} else if(device == thisDeviceClass_iPhone6plus) {
[self.imageView setFrame:CGRectMake(0,0, 414, 255)]; //233
offset = 40;
}
Your approach is very old school. You are "hard coding" the size of the image which means that if next year Apple came up with a new iPhone that have, yet again, a different size you'll be in trouble. You should consider using auto-layout constrains which is Apple's recommended approach (here is a good tutorial: http://www.raywenderlich.com/50317/beginning-auto-layout-tutorial-in-ios-7-part-1).
You can also set the ImageView.contentMode to UIViewContentModeScaleAspectFill which will do the crop and resize for you.

Objective C - Set ImageView Width and Maintain Aspect Ratio

I am using the following animation to show a full screen image of a thumbnail onto the view:
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[testImageView setFrame:CGRectMake(0, 0, testImage.size.width, testImage.size.height)];
}completion:^(BOOL finished){
}];
However, this will not contain the image within the bounds of the screen if it is a large image.
Is there a way that I can set the width of the image to the width of the screen, and adjust the image's height then to keep the proper aspect ratio?
Like the above method but with a bit more modification:
CGFloat verticalScale = image.height / bounds.height;
CGFloat horizontalScale = image.width / bounds.width;
CGFloat scale = max(verticalScale,horizontalScale);
CGFloat imageViewHeight = image.height / scale;
CGFloat imageViewWidth = image.width / scale;
CGRect imageViewFrame = CGRectMake(x: DESIRE_X, y: DESIRE_Y, width:imageViewWidth ,height:imageViewHeight)
imageView.frame = imageViewFrame;
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect bounds = [UIScreen mainScreen].bounds;
[testImageView setFrame:bounds];
testImageView.contentMode = UIViewContentModeScaleAspectFit;
But there may be some paddings if the image's scale is not equal to screen's.If you prefer the image to cover the full screen, then change contentMode to UIViewContentModeScaleAspectFill.
Please try this below code:
testImageView
=[[UIImageView alloc]initWithFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y ,self.view.frame.size.width, self.view.frame.size.height
)]

Constant crop aspect ratio using PEPhotoCropEditor library in iOS

I'm using PEPhotoCropEditor library in one of my iOS project. I able to having constant aspect ratio (1:1) with following code.
- (void)openEditor {
PECropViewController *controller = [[PECropViewController alloc] init];
controller.delegate = self;
controller.image = self.imageView.image;
UIImage *image = self.imageView.image;
CGFloat width = image.size.width;
CGFloat height = image.size.height;
CGFloat length = MIN(width, height);
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
controller.imageCropRect = CGRectMake((width - length) / 2,
(height - length) / 2,
length,
length);
// Restricted to a square aspect ratio
controller.keepingCropAspectRatio = YES;
controller.cropAspectRatio = 1.0;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navigationController animated:YES completion:NULL];
}
But then I needed to have aspect ratio (height:width) of (1:3) instead of (1:1).
controller.cropAspectRatio = 1.0f / 3.0f;
I tried above and nothing happened. Still having same aspect ratio. Then I change below code too.
controller.imageCropRect = CGRectMake((width - length) / 2,
(height - length) / 2,
length * 3,
length);
Again nothing happened. After commenting above I hard coded the value like below.
controller.imageCropRect = CGRectMake(0.0f, 0.0f, 300.0f, 100.0f);
Then the cropping area displayed correctly but when resizing that aria will lead to destroy the crop aspect ratio (1:3)
Any idea how to remain aspect ratio (1:3) till all editing process complete?
Set this in ViewdidAppear in PECropViewController.m
self.cropAspectRatio = 1.0f;
self.keepingCropAspectRatio = YES;
And pass value to function setKeepingAspectRatio in PECropRectView.m
- (void)setKeepingAspectRatio:(BOOL)keepingAspectRatio
{
_keepingAspectRatio = keepingAspectRatio;
if (self.keepingAspectRatio) {
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
self.fixedAspectRatio = your_fixed_aspect_ratio_value;//fminf(width / height, height / width);
}
}
you will get expected result.

How to make imageView fixed size and with rounded corners

I'm using Parse.com to build a simple app. I want to know if there is any way to make the imageView size fixed (for example 30x30px) and round the corners of the image?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
PFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
PFUser *user = users[indexPath.row];
cell.textLabel.text = user[PF_USER_FULLNAME];
PFImageView *imageView = [[PFImageView alloc] init];
[imageView setBackgroundColor:[UIColor clearColor]];
cell.imageView.image = [UIImage imageNamed:#"tab_profile.png"]; // placeholder image
cell.imageView.file = (PFFile *)user[PF_USER_THUMBNAIL]; // remote image
[cell.imageView loadInBackground];
return cell;
}
Please help with any advice, I'm new to Xcode and Parse SDK...
1)
2)
This is what I usually use to get a circle on an imageView:
Crop (not resize) the image to make it a square. The method below resizes the image to whatever size you pass as a CGSize:
(UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
double ratio;
double delta;
CGPoint offset;
//make a new square size, that is the resized imaged width
CGSize sz = CGSizeMake(newSize.width, newSize.width);
//figure out if the picture is landscape or portrait, then
//calculate scale factor and offset
if (image.size.width > image.size.height)
{
ratio = newSize.width / image.size.width;
delta = (ratio*image.size.width - ratio*image.size.height);
offset = CGPointMake(delta/2, 0);
} else {
ratio = newSize.width / image.size.height;
delta = (ratio*image.size.height - ratio*image.size.width);
offset = CGPointMake(0, delta/2);
}
//make the final clipping rect based on the calculated values
CGRect clipRect = CGRectMake(-offset.x, -offset.y,
(ratio * image.size.width) + delta,
(ratio * image.size.height) + delta);
//start a new context, with scale factor 0.0 so retina displays get
//high quality image
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
} else {
UIGraphicsBeginImageContext(sz);
}
UIRectClip(clipRect);
[image drawInRect:clipRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Then, call the method just created, and apply the corner radius (in this case it will be a circle):
imageView.image = [self squareImageWithImage:imageNormal scaledToSize:CGSizeMake(50, 50)]; // assuming you want a 50x50px image
// convert imageview to circle
imageView.layer.cornerRadius = cell.imageView.frame.size.width / 2;
imageView.clipsToBounds = YES;
You can reduce the amount of the cornerRadius to something else with this same call.
This is how I do it:
Image with fix size
If it's possible use Storyboard and create 2 size constraints for the PFImageView with the 30 px value, but if the project doesn't uses size classes it should work without constraints, just set the proper size for the image. If you experience shape changes you could play with View Mode also.
TooManyEduardos's solution for the rounded image is correct, if you don't want circle just play with the numbers.
//1
imageView.layer.cornerRadius = cell.imageView.frame.size.width / 2;
//2
imageView.layer.cornerRadius = cell.imageView.frame.size.width / 4;
//3
imageView.layer.cornerRadius = cell.imageView.frame.size.width / 6;
//4
imageView.layer.cornerRadius = cell.imageView.frame.size.width / 8;

Is it possible to resize the camera preview in iOS?

I'm trying to make the camera preview take up part of my view, rather than the entire screen. Is this even possible? Do I have to use something like the AV Foundation Framework to do this? I am new to iOS development, so any guidance would be greatly appreciated! Thanks!
Yes, you need to use AVFoundation for that. In the past some people (including myself) have tried to use UIImagePickerController instead (by replacing its view property with a custom view), but it doesn't work in iOS 7 (or at least will never work as you expect). Specifying a custom preview was never a documented feature of UIImagePickerController to begin with, so no big surprise.
Try this code to maintain your resultant image in same size. Its working well for me.
Step 1: First create IBOutlet for UIImageview.
Step 2: Add custom method into your imagePickerController.
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
[self dismissViewControllerAnimated:YES completion:nil];
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
OriginalImage=info[UIImagePickerControllerOriginalImage];
image = info[UIImagePickerControllerOriginalImage];
//------Additional method placed here----
imageview.image = image; // additional method
[self resizeImage];
//---------
if (_newMedia)
UIImageWriteToSavedPhotosAlbum(image,self,#selector(image:finishedSavingWithError:contextInfo:),nil);
}
else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
{
// Code here to support video if enabled
}
}
Step 3: Custom method to resize images on camera preview
//---- Resize the original image ----------------------
-(void)resizeImage
{
UIImage *resizeImage = imageview.image;
float width = 320;
float height = 320;
CGSize newSize = CGSizeMake(320,320);
UIGraphicsBeginImageContextWithOptions(newSize,NO,0.0);
CGRect rect = CGRectMake(0, 0, width, height);
float widthRatio = resizeImage.size.width / width;
float heightRatio = resizeImage.size.height / height;
float divisor = widthRatio > heightRatio ? widthRatio : heightRatio;
width = resizeImage.size.width / divisor;
height = resizeImage.size.height / divisor;
rect.size.width = width;
rect.size.height = height;
//indent in case of width or height difference
float offset = (width - height) / 2;
if (offset > 0) {
rect.origin.y = offset;
}
else {
rect.origin.x = -offset;
}
[resizeImage drawInRect: rect];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
imageview.image = smallImage;
imageview.contentMode = UIViewContentModeScaleAspectFit;
}

Resources