15

I am interested in using a custom overlay for the UIImagePickerController for camera controls that take the entire screen, very similar to what the default Camera app does when switched to the video function.

I have referenced several questions here on SO on this topic, particularly this one, as well as the Apple example on how to use a custom overlay for the UIImagePickerController, but none of them have been able to produce a successful, full-screen result with iOS 7.

So far, this is what the camera UI looks like with the custom overlay (without any buttons):

enter image description here

Note that there is a black bar in the bottom of the screen. I noticed that I could get the black bar removed if I change the cameraAspectRatio to 1, but this distorts the camera zoom as it then has a very zoomed in view. Is there a way to have full screen without either distorting the zoom too much (I understand that a small bit is necessary), and also remove the black bar?

Here is my code that configures the custom overlay:

{
        self.imagePickerController.showsCameraControls = NO;

        [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
        self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
        self.imagePickerController.cameraOverlayView = self.overlayView;
        self.overlayView = nil;

        // Device's screen size (ignoring rotation intentionally):
        CGSize screenSize = [[UIScreen mainScreen] bounds].size;


        float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
        float imageWidth = floorf(screenSize.width * cameraAspectRatio);
        float scale = ceilf((screenSize.height / imageWidth) * 10.0) / 10.0;

        self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);

    }

Update:

An additional issue that I noticed is, the picture that is shown in camera preview is always smaller and has less details than the picture that is saved when the method [self.imagePickerController takePicture]. To illustrate:

This is what the keyboard looks like in the camera preview with the black bar below (sorry its a bit shaky):

enter image description here

However, note that the actual captured image in the preview panel has a lot more details as shown here, especially towards the top, as well as both left and right.

enter image description here

My question is, how would be able to set my camera preview so that what the user sees in preview is exactly the image that it will capture and could be shown to them afterwards? Thanks!

Update 2

Here is the entire code in viewDidLoad that sets up the camera controls.

- (void)viewDidLoad
{
    [super viewDidLoad];

    //Appearance configuration
    self.navigationItem.hidesBackButton = YES;

    //UIImagePickerController initialization and setup
    self.imagePickerController = [[UIImagePickerController alloc] init];
    self.imagePickerController.delegate = self;
    self.imagePickerController.allowsEditing = NO;
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
        self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; //if there is a camera avaliable
    } else {
        self.imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;//otherwise go to the folder
    }
    self.imagePickerController.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeImage, nil];
    if (self.imagePickerController.sourceType == UIImagePickerControllerSourceTypeCamera)
    {
        self.imagePickerController.showsCameraControls = NO;

        [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
        self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
        self.imagePickerController.cameraOverlayView = self.overlayView;
        self.overlayView = nil;

        // Device's screen size (ignoring rotation intentionally):
        CGSize screenSize = [[UIScreen mainScreen] bounds].size;

        int heightOffset = 0;

        if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
        {
            heightOffset = 120; //whole screen :)
        }

        float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
        float imageWidth = floorf(screenSize.width * cameraAspectRatio);
        float scale = ceilf(((screenSize.height + heightOffset) / imageWidth) * 10.0) / 10.0;

        self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);

    }
}

And in viewWillAppear, I call the image picker view:

BOOL modalPresent = (BOOL)(self.presentedViewController);
    //Present the Camera UIImagePicker if no image is taken
    if (!appDelegate.imageStorageDictionary[@"picture1"]){
        if (modalPresent == NO){ //checks if the UIImagePickerController is modally active
            [self presentViewController:self.imagePickerController animated:NO completion:nil];
        }
    }
Community
  • 1
  • 1
daspianist
  • 5,336
  • 8
  • 50
  • 94
  • 1
    Hey.. Can you please tell me how did you implement the "Take Next Photo" feature? I want my camera to be in capture mode even after photo has been clicked. – Smit Yash Dec 22 '15 at 11:48
  • @daspianist Did you find a solution for this as I am also facing a similar issue. There a black bar in the bottom which appears – Khadija Daruwala May 27 '21 at 07:52

9 Answers9

26

After many attempts, this is what worked for me with many thanks to other people's suggestions. The following facts were very helpful to know and keep in mind:

The camera's points resolution is 426 * 320. In order for the camera preview's height to be stretched to the phone's screen height of 568, it needs to be multiplied by a factor of 1.3333 when using CGAffineTransformScale.

Note that the below are hard coded with various numbers based on the iPhone 5's screen resolution in points. They could be improved by using such objects such as screen.height, screen.width and other variables to make it applicable to iPhone 4/4s dimensions as well.

    self.imagePickerController.showsCameraControls = NO;

    [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
    self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
    self.imagePickerController.cameraOverlayView = self.overlayView;
    self.overlayView = nil;

    //For iphone 5+
    //Camera is 426 * 320. Screen height is 568.  Multiply by 1.333 in 5 inch to fill vertical
    CGAffineTransform translate = CGAffineTransformMakeTranslation(0.0, 71.0); //This slots the preview exactly in the middle of the screen by moving it down 71 points
    self.imagePickerController.cameraViewTransform = translate;

    CGAffineTransform scale = CGAffineTransformScale(translate, 1.333333, 1.333333);
    self.imagePickerController.cameraViewTransform = scale;
daspianist
  • 5,336
  • 8
  • 50
  • 94
  • 1
    if you stretch it all out so that the height fits, what happens to the width? – Rob Mar 04 '14 at 16:59
  • The left and right sides are truncated. For example, if you open the default camera app and switch to video, the filled up screen with the dimensions is exactly what you'd see if you applied this code. – daspianist Mar 04 '14 at 17:17
  • Is there a way to accomplish this without using a custom overlay? With the stock camera controls... – Jacob Jul 23 '14 at 04:45
  • @Jacob this tutorial (http://www.appcoda.com/ios-programming-camera-iphone-app/) gives a very good overview on how to use the standard iOS camera controls. – daspianist Jul 23 '14 at 04:48
  • @daspianist thanks. Is that camera utilizing the full screen on the 4 inch devices, though? – Jacob Jul 23 '14 at 04:51
  • Nope, which is why you need to use the custom overlay method described here. The default iOS camera controls do not utilize the full screen - for example, if you opened the `Camera` app on your phone, you will see that the viewfinder is only part of the screen. This is what you will see when using the default implementation as well. – daspianist Jul 23 '14 at 04:53
  • @daspianist Ahh ok, thanks. How would I go about creating a custom overlay who's buttons call the picker's delegate methods? – Jacob Jul 23 '14 at 04:56
  • For the buttons, you would create a custom `overlayView.xib` (using it alongside your main Storyboard) to add the buttons, etc. And you would link them up with `IBActions` and `IBOutlets` with the view controller that will present the Image Picker. For example, if you have `CameraViewController` that executes the bit of code above, it's header file would be where you link all the buttons from your custom `overlayView.xib` – daspianist Jul 23 '14 at 05:01
  • @daspianist now how would I call methods like imagePickerController: (UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info with my picker when my buttons are tapped? – Jacob Jul 23 '14 at 05:22
  • @daspianist: Can you please edit this answer to make it generic for iphone 4s size as well. – Rashmi Ranjan mallick Sep 27 '14 at 03:57
  • And I am facing issue on iOS 8 with above method. Please help. – Rashmi Ranjan mallick Sep 27 '14 at 04:09
  • @daspianist can you tell me from where you will get this? " Camera is 426 * 320 ". cause this solution is not working in iphone 5C. if you know solution for this. please guide me. Thank you. – Urmi Dec 06 '16 at 02:21
  • what is `overlayView` ???????????? where do you declare it, where's it coming from??? Please, make your answer complete next time. – ekashking Oct 16 '21 at 15:47
9

In Swift

var picker: UIImagePickerController = UIImagePickerController();

picker.sourceType = UIImagePickerControllerSourceType.Camera;

picker.showsCameraControls = false;

var screenBounds: CGSize = UIScreen.mainScreen().bounds.size;

var scale = screenBounds.height / screenBounds.width;

picker.cameraViewTransform = CGAffineTransformScale(picker.cameraViewTransform, scale, scale);
Vikramaditya
  • 5,444
  • 6
  • 34
  • 45
  • I tried this code and it does make my default ios camera transparent. But the only problem is the view you look while you are taking a photo is way more magnified that the preview of a taken picture. Do you know how to solve this matter? – Kahsn Jul 10 '15 at 06:26
  • Have you tried scale value as, var scale = screenBounds.width/screenBounds.height; – Vikramaditya Jul 10 '15 at 06:41
  • I just tried it and it gives me very small screen of camera. I heard something like I should make the camera screen bigger and crop out the rest part which doesn't fit the phone screen size in order to make it like snapchat camera view. Not sure what scale I should use and crop out the rest part. – Kahsn Jul 10 '15 at 20:05
2

Make sure that you account for 20px status bar change in iOS7. If you are facing a 20px black screen at bottom of the screen then this will be your issue. You can check that whether the app is running in ios7 or not by one of these preprocessors

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

And you can make following changes to your code and see if it works

{
        self.imagePickerController.showsCameraControls = NO;

        [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
        self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
        self.imagePickerController.cameraOverlayView = self.overlayView;
        self.overlayView = nil;

        // Device's screen size (ignoring rotation intentionally):
        CGSize screenSize = [[UIScreen mainScreen] bounds].size;

        int heightOffset = 0;

        if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
        {
            heightOffset = 20;
        }

        float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
        float imageWidth = floorf(screenSize.width * cameraAspectRatio);
        float scale = ceilf(((screenSize.height + heightOffset) / imageWidth) * 10.0) / 10.0;

        self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);

    }

Please let me know if it works. I haven't coded it to test.

Kamran Khan
  • 1,367
  • 1
  • 15
  • 19
  • 1
    Thanks for suggesting the height offset method. At offset of 20, it had a slight effect, but when I tried 120 it managed to cover the entire bottom black bar. However, the issue is that with a 120 offset, the camera preview becomes very zoomed in, although the pic it takes is still zoomed out. I could provide some screenshots of what the preview vs real pic look like if this helps. – daspianist Jan 06 '14 at 23:06
  • Around 120 is the calculation I was getting also just with doing the math on the screen size. That's how much it has to be zoomed in to fill the screen top to bottom with the aspect ratio the camera naturally captures... your options are either to crop the picture you take to match what you show them in the preview, distort the image so that it stretches vertically to fill the screen on an iPhone5, or to center the image so you have smaller bars at the top and bottom. Personally I'd recommend the bars, which would go away on an iPhone 4 (or 4s) anyway. – Kendall Helmstetter Gelner Jan 07 '14 at 03:58
  • Judging from the first screenshot the self.imagePickerController is aligned to top of the screen as well. I will try to write a code and test it here and will send you. – Kamran Khan Jan 07 '14 at 06:08
  • Also check this post http://stackoverflow.com/questions/19046070/uiimagepickercontroller-in-ios-7-status-bar – Kamran Khan Jan 07 '14 at 06:16
  • @daspianis is the autolayout property enabled for the views? I have tested this code and its working fine https://developer.apple.com/library/ios/samplecode/PhotoPicker/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010196 – Kamran Khan Jan 07 '14 at 06:25
  • If possible please share your full code of the class which is loading the camera control, or of the project. It will be easy to look at it and fix the issue. – Kamran Khan Jan 07 '14 at 06:26
  • 1
    @kamran I did enable autolayout for the views. The camera view does become fully stretched (which is great - thanks for this suggestion), and so now I am working on figuring how which part of the image to crop so that it will look exactly like the image preview in the camera view. – daspianist Jan 07 '14 at 16:55
  • I also added the full code for my `UIImagePickerController` setup in the body of the question. – daspianist Jan 07 '14 at 16:55
1

Because the screen aspect ratio is different than the 4/3 ratio of the camera itself, you need (as you mentioned) to slightly crop the edges in order to have the image extend to the bottom.

In order to do that, you need to have the width be wide enough so that the height can be the full screen. So, your image width and height must be:

float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
float imageWidth = screenSize.height / cameraAspectRatio;

You'll also probably want the view presenting the camera image to be that width that extends off both sides of the screen.

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • Thanks for sharing this result. Unfortunately using it still results in a black bar show in the bottom of the screen. The overlay view presenting the camera preview image is currently set to fit the entire screen (320 * 568) - do you mean that this view should be stretched to be larger than this? – daspianist Jan 06 '14 at 05:40
  • I think so, yes. It could be the current view is set to aspect fit. – Kendall Helmstetter Gelner Jan 06 '14 at 06:58
  • Thanks for your suggestion. I ensured that both the overlay view (from the xib) is shown as scale to fill, and stretched the view it to various sizes to test things out, including very large sizes such as 480*680. However, I still get the black bar in the bottom. – daspianist Jan 06 '14 at 23:14
1

There is a way easier method to get your overlay fullscreen at least tested in iOS 9.

let view    = ... your overlayView
let bounds  = UIScreen.mainScreen().bounds
view.center = CGPoint(x: bounds.midX, y: bounds.midY)
view.bounds = bounds
self.imagePicker.cameraOverlayView = view
Nicolas Manzini
  • 8,379
  • 6
  • 63
  • 81
0

I don't know why you use screen size. Just try this simple code:

if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera])

{

     UIImagePickerController *controller = [[UIImagePickerController alloc] init];
     controller.sourceType = UIImagePickerControllerSourceTypeCamera;
     controller.allowsEditing = NO;
     controller.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:
     UIImagePickerControllerSourceTypeCamera];
     controller.delegate = self;
     [self presentViewController:controller animated:NO completion:^{}];
}
codercat
  • 22,873
  • 9
  • 61
  • 85
Naeem Paracha
  • 216
  • 2
  • 6
0

Try this code to maintain your resultant image in custom size. I got result by using custom method for getting resultant image as custom 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 for custom image size
     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 by using custom method----------------------

-(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;

}
Rajesh Loganathan
  • 11,129
  • 4
  • 78
  • 90
0

To solve a similar problem, I instantiated a ViewController from storyboard and used the controller's view as camera overlay. I don't know if it's what you're looking for, but i used the following code:

UIImagePickerController *globalPicker = [[UIImagePickerController alloc] init];
globalPicker.delegate = self;
globalPicker.sourceType = UIImagePickerControllerSourceTypeCamera;controller=[self.storyboard instantiateViewControllerWithIdentifier:@"myVC"];
overlayView = controller.view;
UIView *cameraView=[[UIView alloc] initWithFrame:self.view.bounds];
[cameraView addSubview:overlayView];
[globalPicker setCameraOverlayView:cameraView];
Nico.S
  • 753
  • 8
  • 12
0

Try this code, it works fine for me

UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.showsCameraControls = NO;

CGSize screenSize = [[UIScreen mainScreen] bounds].size;
float cameraAspectRatio = 4.0 / 3.0;
float imageHeight = floorf(screenSize.width * cameraAspectRatio);
float scale = screenSize.height / imageHeight;
float trans = (screenSize.height - imageHeight)/2;
CGAffineTransform translate = CGAffineTransformMakeTranslation(0.0, trans);
CGAffineTransform final = CGAffineTransformScale(translate, scale, scale);
cameraUI.cameraViewTransform = final
;
cameraUI.delegate = self;
[self presentViewController:cameraUI animated:YES completion:nil];

This code assumes that the aspect of the original camera preview area is 4:3.