Disable camera rotation in iOS? - ios

My code to add a UIImagePickerController:
picker = [[[UIImagePickerController alloc] init] autorelease];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.showsCameraControls = NO;
[self displayContentController:picker inView:cameraBoxView];
[self.view sendSubviewToBack:picker.view];
- (void)displayContentController:(UIViewController *)content inView:(UIView *)v {
//Delete the previous instance of the same view controller
for (UIViewController *vc in self.childViewControllers) {
if ([vc isKindOfClass:[content class]]) {
[vc removeFromParentViewController];
}
}
[self addChildViewController:content];
content.view.frame = v.frame;
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];
if ([content isKindOfClass:[CCInnerNavigationController class]]) {
innerNavigationController = (CCInnerNavigationController *)content;
}
}
I have disabled all the device orientations except of portrait. But the image from camera is rotated. How to solve this issue in both iOS 6 and 7.
P.S. I understand that there are a lot of soludions but it seems they are too old because NOTHING of them works.

When you instatiate the UIImagePickerController you must also pass it a delegate. The delegate should be conform to 2 protocols UIImagePickerControllerDelegate and UINavigationControllerDelegate. The second makes possible to add some logic for rotation at runtime, implement this methods accordingly to what you want to achieve.
– navigationControllerPreferredInterfaceOrientationForPresentation:
– navigationControllerSupportedInterfaceOrientations:

I changed the following code:
imgView.image = img;
to:
imgView.image = [UIImage imageWithCGImage:img.CGImage scale:1 orientation:UIImageOrientationRight];
And now it works in both iOS 6 and 7. I don't know how it fixes this bug but it really works. Maybe image picker corrupts the image or image view and this code fixes it by creating a new image instance.

Related

UIButton inside UIImagePickerController.cameraOverlayView not responding

I have got a UIButton inside a UIView that I set as the cameraOverlayView.
I also scaled the uiimagepicker cameraView to cover the whole screen (as you can see below in the picture).
Here's my code :
// CameraViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if(!self.imagePickerController)
[self setupCamera];
}
-(void)setupCamera{
self.imagePickerController = [[UIImagePickerController alloc] init];
self.imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
self.imagePickerController.delegate = self;
self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.imagePickerController.showsCameraControls = NO;
self.imagePickerController.cameraOverlayView = self.overlayView;
[self scaleCameraPreview];
// Present picker in the next loop (to avoid warning - "Presenting view controllers on detached view controllers is discouraged")
[self performSelector:#selector(presentPicker) withObject:nil afterDelay:0.0];
}
-(void)presentPicker{
[self presentViewController:self.imagePickerController animated:NO completion:nil];
}
-(void)scaleCameraPreview{
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
int heightOffset = 0;
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
heightOffset = 120;
}
float cameraAspectRatio = 4.0 / 3.0;
float imageWidth = floorf(screenSize.width * cameraAspectRatio);
float scale = ceilf(((screenSize.height + heightOffset) / imageWidth) * 10.0) / 10.0;
self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);
}
-(IBAction)cameraButtonPressed:(id)sender{
[self.imagePickerController takePicture];
}
And this is my hierarchy of viewcontrollers:
- UINavigationController
- SWRevealViewController (manages a drawer functionality between different controllers)
- UINavigationController (current controller for the camera)
- CameraViewController
- PickerViewController (Presented modally as you can see above)
- PhotoTakenViewController (Controller that will fire after UIImagePicker returns an image)
EDIT: Adding overlay info
I have searched online for similar threads (e.g. UIImagePickerController Overlay Buttons Not Firing) but didn't find any solution.
The UIButton that you can see in the picture, is inside a UIView this is the overlayView in the code assigned to the cameraOverlayView. It is also connected to the cameraButtonPressed: action through an outlet, and it works perfectly if I don't add the UIImagePickerController.
It isn't responding to any touches I don't know why.
Does anyone know what's going on?
I've fixed my issue but I believe in a sketchy way:
[self presentViewController:self.imagePickerController animated:NO completion:^(void){
// Re-add the button so it is on top of the overlay
[self.imagePickerController.view addSubview:self.takePhotoButton];
}];
Will probably come back later to this issue and post an update in case I find a better solution.

iOS 7 crashes BAD_ACCESS when using UIImagePickerController

I am trying to use a UIImagePickerController to let my users select photos or take photos to be used in my app. But the app seems to crash most of the time whenever a image is selected from their photos or when they choose to use a photo taken. I used NSZombie objects and I get this message:
[NSISRestrictedToNonNegativeVariable retain]: message sent to deallocated instance 0x168c3530
I don't know what is going on here. This same problem occurred when I was presenting a modal view occasionally.
Here is my code for presenting the UIImagePickerController and handling the response:
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:CANCEL_TITLE]) {
return;
}
UIImagePickerControllerSourceType sourceType = buttonIndex == 0 ? UIImagePickerControllerSourceTypePhotoLibrary : UIImagePickerControllerSourceTypeCamera;
UIImagePickerController* controller = [[UIImagePickerController alloc] init];
controller.sourceType = sourceType;
[controller setDelegate:self];
if (buttonIndex == 1) {
//PNCoverOverlayView* overlayView = [PNCoverOverlayView viewWithNibNamed:nil];
//[controller setCameraOverlayView:overlayView];
[controller setShowsCameraControls:YES];
}
[self presentViewController:controller animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker dismissViewControllerAnimated:YES completion:^{
UIImage* originalImage = info[UIImagePickerControllerOriginalImage];
_imageToUpload = originalImage;
CGFloat scaleAdjust = ([[UIScreen mainScreen] respondsToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2) ? 0.5 : 1;
CGFloat length = LENGTH * scaleAdjust;
if ([picker sourceType] == UIImagePickerControllerSourceTypeCamera) {
PNCoverOverlayView* overlay = (PNCoverOverlayView*)[picker cameraOverlayView];
CGSize overlaySize = [overlay frame].size;
UIImage* cropped = [UIImage imageCroppedToSize:overlaySize fromImage:originalImage];
_imageToUpload =[UIImage imageResizedToSize:CGSizeMake(length, length) fromImage:cropped];
} else {
_imageToUpload = [UIImage imageScaledToMaximumLength:length fromImage:originalImage];
}
[self performSegueWithIdentifier:#"reviewPhotoUpload" sender:self];
}];
}
I commented out adding a custom overlay to the UIImagePickerController, because that fixed the app crashing when presenting the UIImagePickerController
You should check for the following things:
Are you testing it on device ?
Follow the steps mentioned under Overview in this apple guideline, and take care of the device on which you are running. e.g.,
iPhone or iPad.
Check for isSourceTypeAvailable also.
Also, Check for isCameraDeviceAvailable property.
When trying to get cameraOverlayView, you should check if its not nil. See the documentation of this property for more
information.
Hope it helps!
You are instantiating the controller in your function:
UIImagePickerController* controller = [[UIImagePickerController alloc] init];
You should keep a strong reference to the controller in your class, an ivar would be a good choice for that.

Presenting camera roll UIImagePickerController over open camera

On my app I have a cameraOverlayView over my open camera with custom controls for the camera buttons. The app allows the user to take several pictures before closing the camera, so the shutter button does not call dismissViewControllerAnimated, instead there's a close button for when you're done taking pictures.
Now, one of the buttons on the camera overlay is a gallery button to allow the user to pick a saved image instead of shooting a new one. I've tried two different approaches to make this work, both failed.
First approach
Use the same UIImagePickerController instance that is currently presenting the overlay and switch the sourceType to library. It does present the gallery then, but when a photo is tapped, I can't dismiss the galley without dismissing the whole overlay.
Second approach
Create a separate instance of UIImagePickerController, set the sourceType to gallery and attempt to call presentViewController, which then fails with the warning:
"Warning: Attempt to present on
whose view is not in the window
hierarchy!"
Does anyone have a solution for this issue? Is this even possible?
Try this~~ I think it is your goal.
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) UIImagePickerController *imagePicker;
#end
#implementation ViewController
#synthesize imagePicker = _imagePicker;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
sleep(2);
_imagePicker = [[UIImagePickerController alloc] init];
[_imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
[_imagePicker setDelegate:self];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 50, 100, 30)];
[button setTitle:#"Library" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor darkGrayColor]];
[button addTarget:self action:#selector(gotoLibrary:) forControlEvents:UIControlEventTouchUpInside];
[_imagePicker.view addSubview:button];
[self presentViewController:_imagePicker animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)gotoLibrary:(id)sender
{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker.view setFrame:CGRectMake(0, 80, 320, 350)];
[imagePicker setSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
[imagePicker setDelegate:self];
[_imagePicker presentViewController:imagePicker animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker dismissViewControllerAnimated:YES completion:nil];
}
#end
The second approach is correct.
I don't see your code, but I think that your controller hierarchy is something similar to this:
Main VC ---present---> Camera VC
so, if you call
[self presentViewController:picker animated:YES completion:^{}];
from your Main VC, you are attempting to show another VC from an "hidden" one (covered by the Camera VC).
The key is to take a reference to your camera VC (let's call it cameraVC) and do something similar from Main VC:
[cameraVC presentViewController:theOtherPicker animated:YES completion:^{}];
doing this, the "present" action is done by the Camera VC (visible) without warnings, and not by the hidden Main VC.
You could code your own custom gallery views for selecting photos and then add that to the cameraOverlayView subview hierarchy.
There must be open source projects on GitHub, which show how to make these views, somewhere. Alternatively, I happen to have released a control very similar to what you are looking for if you don't want to start from scratch.
It's quite a simple procedure really — a collection view with a datasource backed by the AssetsLibrary framework.
I would set up a capture session as follows:
- (void)setupCaptureSession
{
NSError* error = nil;
// Create the session
_captureSession = [[AVCaptureSession alloc] init];
_captureSession.sessionPreset = AVCaptureSessionPresetMedium;
AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
[_captureSession addInput:input];
AVCaptureVideoDataOutput* output = [[AVCaptureVideoDataOutput alloc] init];
[_captureSession addOutput:output];
// Configure your output.
dispatch_queue_t queue = dispatch_queue_create("myCameraOutputQueue", NULL);
//If you want to sebsequently use the data, then implement the delegate.
[output setSampleBufferDelegate:self queue:queue];
}
Having done that, you can create a preview layer as follows:
_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
[_captureSession startRunning];
And then add the preview layer to your view:
_myView.layer addSubLayer:_previewLayer];
Now that you have this set up, I'd add a custom image picker, such as this one: https://github.com/arturgrigor/AGImagePickerController
I present a custom camera view controller, onto which I add a "SourceTypeCamera" UIImagePickerController as a contained child view controller. My custom view controller has a button that in turn adds another UIImagePickerController instance, this one a "SourceTypePhotoLibrary".
You end up with a nested view controller hierarchy:
root/main view controller — presents:
my custom camera view controller – adds as child view controller:
either camera or photo-library UIImagePickerController
Something like this:
- (void)viewDidLoad { (id<UIImagePickerControllerDelegate,UINavigationControllerDelegate>)theDelegate {
[super viewDidLoad];
[self startImagePickerWithSourceType:UIImagePickerControllerSourceTypeCamera
delegate:self];
// configure button in camera overlay to call -pickPhotoTapped
}
- (void) pickPhotoTapped {
[self startImagePickerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary
delegate:self];
}
- (BOOL) startImagePickerWithSourceType:(UIImagePickerControllerSourceType)sourceType
delegate:(id<UIImagePickerControllerDelegate,UINavigationControllerDelegate>)theDelegate {
if (([UIImagePickerController isSourceTypeAvailable:sourceType] == NO)
|| (theDelegate == nil))
return NO;
self.cameraUI = [[UIImagePickerController alloc] init];
self.cameraUI.sourceType = sourceType;
self.cameraUI.view.frame = self.view.bounds;
self.cameraUI.delegate = theDelegate;
if (sourceType == UIImagePickerControllerSourceTypeCamera) {
self.cameraUI.allowsEditing = NO;
self.cameraUI.showsCameraControls = NO;
self.cameraUI.cameraOverlayView = [self overlayView];
}
[self addChildViewController:self.cameraUI];
[self.view addSubview:self.cameraUI.view];
[self.cameraUI didMoveToParentViewController:self];
return YES;
}

Subview common to multiple view controllers is unresponsive

I am using a UIView to show busy/processing clue to User and components in loading view are not responding to user touches. Let me elaborate my problem.
I have around 3 UIViewControllers and based on situation one View from VieControllers would be displayed. I use following code to change current view
UIView *currentView = self.view;
UIView *theWindow = [currentView superview];
//Some other code
[currentView removeFromSuperview];
[self.view setFrame: [tileCountViewController.view bounds]];
[theWindow addSubview:tileCountViewController.view];
[theWindow setFrame:[tileCountViewController.view bounds]];
[theWindow setFrame: [[UIScreen mainScreen] bounds]];
I then have a Loading view which generally used to show busy/processing kinda of UI to User. I created Loading view in following way from nib file
- (id)init {
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
NSString *nibName = #"LoadingViewController";
if(UIInterfaceOrientationIsLandscape(orientation)) {
nibName = #"LoadingView-landscape";
}
else {
nibName = #"LoadingView";
}
UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
UIView *portraitView = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
self = [super initWithFrame:portraitView.frame];
[self addSubview:portraitView];
[self bringSubviewToFront:portraitView];
return self;
}
In app delegate, I initialize loading view and add it to Main window
if(!loadingViewIndicator){
loadingViewIndicator = [[LoadingView alloc]init];
}
[[[UIApplication sharedApplication]keyWindow]addSubview:loadingViewIndicator];
loadingViewIndicator.hidden = YES;
Whenever I need to show loading/busy view, I simply load it like this
[loadingViewIndicator performSelectorOnMainThread:#selector(startLoadingViewIndicator:) withObject:startMsg waitUntilDone:NO];
- (void)startLoadingViewIndicator:(NSString *)startMsg{
self.hidden = NO;
[self.superview bringSubviewToFront:self];
[self.activityIndicator startAnimating];
[self.loadingMessage setText:startMsg];
self.loadingProgressView.progress = 0.0f;
[self refreshPosition];
}
Everything works fine but components in Loading view are not responding to touches. Any clue what is going wrong?
I just realized that problem is due to threading issue. I show this subview when User starts downloading any file. My earlier code was to directly start download
urlconnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately: YES];
I am now calling this method from another method using background thread as below
[self performSelectorInBackground:#selector(startDownload) withObject:nil];
And then I am adding runloop in download like
urlconnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately: YES];
CFRunLoopRun();
This works fine and my subview is responsive. It really took lots of time. The problem is there is clear code example to help to run download in background thread and at the same time make UI responsive.

iOS 5 Splash Page Transition

My client wants his splash image to fade out and then show the UI. I am not sure how to do this. As of right now it just appears once it loads. Is there a simple way to do this ?
UIImageView *splash=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"splash"]];
[UIView animateWithDuration:1
animations:^{
splash.alpha = 0.0;
}
completion:^(BOOL finished)
{
[splash release];
}];
I was looking at this tutorial but decided not to implement it so I cant vouch as to whether it works or not but everything looks in check
http://www.dobervich.com/2010/10/22/fade-out-default-ipad-app-image-with-proper-orientation/
Beware though adding to much to your app startup is grounds for app rejection.
Avoid displaying an About window or a splash screen. In general, try
to avoid providing any type of startup experience that prevents people
from using your application immediately.
Source: Apple Developer Site
As I just explained on a closely related question, don't use splash screens! Please relate this to your client, who might not be familiar with the Human Interface Guidelines.
That said, if you can't convince you client otherwise, you can just fade out the UIImageView that I mentioned in my other response.
You can implement your own UIViewController as a splash screen:
#interface SplashScreenViewController : UIViewController {
UIImageView *splashView;
}
#end
//
#import "SplashScreenViewController.h"
#implementation SplashScreenViewController
#pragma mark - View lifecycle
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[splashView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self.view addSubview:splashView];
}
- (void)viewWillAppear:(BOOL)animated {
if (isIPad) {
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]))
splashView.image = [UIImage imageNamed:#"Default-Portrait~ipad.png"];
else
splashView.image = [UIImage imageNamed:#"Default-Landscape~ipad.png"];
}
else
splashView.image = [UIImage imageNamed:#"Default.png"];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (isIPad) {
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation))
splashView.image = [UIImage imageNamed:#"Default-Portrait~ipad.png"];
else
splashView.image = [UIImage imageNamed:#"Default-Landscape~ipad.png"];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (isIPad ? YES : UIInterfaceOrientationIsPortrait(interfaceOrientation));
}
#end
Then, after showing, you can hide this UIViewController with any transition you want.

Resources