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;
}
Related
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.
I am building an iOS app that is to have a full screen ImagePickerController, with the resulting image that is captured to be the same that is shown in the ImagePickerController View. This is my current relevant code:
To create and transform the ImagePickerController:
self.imagePicker = [[UIImagePickerController alloc] init];
self.imagePicker.delegate = self;
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// set the aspect ratio of the camera
float heightRatio = 4.0f / 3.0f;
// calculate the height of the camera based on the screen width
float cameraHeight = floorf(screenSize.width * heightRatio);
// calculate the ratio that the camera height needs to be scaled by
float scale = ceilf((screenSize.height / cameraHeight) * 10.0) / 10.0;
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.imagePicker.showsCameraControls = NO;
[self.imagePicker setCameraOverlayView:cameraView];
// move the controller to the center of the screen
self.imagePicker.cameraViewTransform = CGAffineTransformMakeTranslation(0, (screenSize.height - cameraHeight) / 2.0);
// concatenate the scale transform
self.imagePicker.cameraViewTransform = CGAffineTransformScale(self.imagePicker.cameraViewTransform, scale, scale);
}
Once the image captured, here is the code I am using to redraw the captured image to match the Preview:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
self.image = [info objectForKey:UIImagePickerControllerOriginalImage];
self.image = [self croppedImage:self.image];
- (UIImage *) croppedImage: (UIImage *)image{
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// set the aspect ratio of the camera
float heightRatio = 4.0f / 3.0f;
// calculate the height of the camera based on the screen width
float cameraHeight = floorf(screenSize.width * heightRatio);
// calculate the ratio that the camera height needs to be scaled by
float scale = ceilf((screenSize.height / cameraHeight) * 10.0) / 10.0;
CGSize originalImageSize = [image size];
CGSize newImageSize = CGSizeMake(floorf(originalImageSize.width / scale)* 3/4, floorf(originalImageSize.height / scale)* 3/4);
CGRect newImageRect = CGRectMake((originalImageSize.width - newImageSize.width)/2.0, (originalImageSize.height - newImageSize.height)/2.0, newImageSize.width, newImageSize.height);
return [image croppedImage:newImageRect];
}
So my problem is that my CroppedImage method is not calculating correctly, as the resulting image seems to be more "zoomed in" than needed. Not really sure what is wrong in the calculation.
Note - this app is to intended to scale properly on all iPhones - Portrait mode only. I am currently testing on iPhone 6.
In case this helps anybody - on my device i was able to fix it by switching
CGSize newImageSize = CGSizeMake(floorf(originalImageSize.width / scale)* 3/4, floorf(originalImageSize.height / scale)* 3/4);
to
CGSize newImageSize = CGSizeMake(floorf(originalImageSize.width / scale), floorf(originalImageSize.height / scale)* 4/3);
only the height/scale needed to by be multiplied and by 4/3, not 3/4. I have not tested this on any other devices yet. Just figured this might help anybody running into the same thing.
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];
}
I'm running into a rendering issue with my tableView UIImages and was wondering if anyone has encountered the same problem and knows how to fix it.
Here is my cellForRowAtIndexPath
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.text = exerciseDisplayName;
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
[tableView setSeparatorInset:UIEdgeInsetsZero];
UtilityMethods *commonMethods = [[UtilityMethods alloc]init];
UIImage *rowImage = [commonMethods imageForRow:tempPlaceholder.bodyPart];
cell.imageView.image = rowImage;
return cell;
}
Here is my height for row.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 96;
}
There are lots of lines and squiggles in the images in the table. I was wondering if anyone knowns any UIImage properties that I might need to apply to my image to fix the problem. Increasing the height for row in table fixes the problem at the expense of increasing the height of the table row. The number that seems to work is 128 in heightForRow. When using 128 the squiggles are much less noticeable. Now I'm pretty sure this has something to do with how iOS is rendering the image. Ive taken the image and resized it to 76x76 using Microsoft Paint just to see if I would see the same problem, and the images appear just fine without all the squiggles. The images are .png format. The original size of the images is 1024x1024. Ive just resized them downwards as I've needed them. If anyone has any tips or advice on how to fix this I'd really appreciate it.
You are going to need to resample the image to the size you need. Viewing a large image in a small space looks rather bad on iOS devices (most any really). But if you use built in functions to create a new UIImage of the proper size everything looks much better. Scaling down a UIImage when displaying will always look worse than creating a new image of the proper size and displaying that. The way to do this is as follows (taken from here):
- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{
UIImage *sourceImage = self;
UIImage *newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO)
{
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor)
{
scaleFactor = widthFactor; // scale to fit height
}
else
{
scaleFactor = heightFactor; // scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor)
{
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}
else
{
if (widthFactor < heightFactor)
{
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
}
UIGraphicsBeginImageContextWithOptions(targetSize, 0, NO); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil)
{
NSLog(#"could not scale image");
}
//pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
That function does a bit more than you are looking for, but you should be able to cut it does to only what you need.
Make sure to use the UIGraphicsBeginImageContextWithOptions function instead of UIGraphicsBeginImageContext so you deal with retina displays properly, otherwise this will make your images more blurry than they should be and you will have a second problem to deal with.
I am currently using the default UIImagePickerController, and immediately after a picture is taken, the following default screen is shown:
My question is, how would I be able to use my own custom UIViewController to view the resultant image (and therefore bypass this confirmation screen ).
Please note that am not interested in using a custom overlay for the UIImagePicker with custom camera controls or images from photo gallery, but rather just to skip this screen and assume that the photo taken is what the user would have liked.
Thanks!
Try this code to maintain your resultant image in same size:
First create IBOutlet for UIImageview.
-(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];
//----------
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
}
}
//---- 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;
}
You can customize the image picker view controller using an overlay view.
Read the documentation here.
You set up a new view, set it as the camera overlay, tell the image picker to not display its own controls and display the picker.
Inside your the code of your overlay, you call takePicture on the image picker view controller to finally take the image when you are ready.
Apple has an example of this here:
https://developer.apple.com/library/ios/samplecode/PhotoPicker/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010196-Intro-DontLinkElementID_2
For transforms, you can use the cameraViewTransform property to set transformations to be used when taking the image.
After taking the picture, the delegate method would be called:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
In this method, you dismiss the imagePickerController. You have access to the image, so you can play around with the image, possibly display it in a UIImageView. So the code would be something like this:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self dismissViewControllerAnimated:YES completion:nil];
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera)
{
//here you have access to the image
UIImage *pickedImage = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
//Create an instance of a view controller and show the UIImageView or do your stuff there. According to you business logic.
}
}