I am making a game made in C++ which at some point wants to open make use of the camera.
To do this it interfaces with an Objective C class which presents the modal view controller:
UIImagePickerController* cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects:(NSString*)kUTTypeImage, nil];
cameraUI.allowsEditing = YES;
cameraUI.delegate = self;
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentModalViewController:cameraUI animated:YES];
This class exposes a State variable to allow the game to watch the progress of the modal view controller.
It is initalized:
State = CAMERA_ACTIVITY_WORKING;
And updated by the UIImageControllerDelegate functions:
- (void)imagePickerControllerDidCancel: (UIImagePickerController*) picker
{
State = CAMERA_ACTIVITY_CANCELED;
[[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)imagePickerController: (UIImagePickerController*) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
State = CAMERA_ACTIVITY_IMAGECAPTURED;
[[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
The modal view controller opens and closes as it should and the delegates definately get called. However, when the game polls the State variable it appears to have not been updated returning CAMERA_ACTIVITY_WORKING.
I have tried making the variable volatile but this had no effect.
Can anybody help?
Edit: Full Class Source Code
.h
#interface Camera : UIViewController<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
volatile State State;
UIImage* CapturedImage;
}
- (void)imagePickerControllerDidCancel: (UIImagePickerController*) picker;
- (void)imagePickerController: (UIImagePickerController*) picker didFinishPickingMediaWithInfo:(NSDictionary *)info;
- (bool) IsAvailable;
- (bool) Show;
- (State) GetState;
#end
.m
#implementation Camera
- (id)init
{
self = [super init];
if(!self) return self;
State = CAMERA_ACTIVITY_WORKING;
CapturedImage = NULL;
return self;
}
- (void)imagePickerControllerDidCancel: (UIImagePickerController*) picker
{
DEBUG_LOG("imagePickerControllerDidCancel");
State = Poppet::ICameraActivity::CAMERA_ACTIVITY_CANCELED;
DEBUG_LOG("State: " + STRING_CAST(State));
[[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)imagePickerController: (UIImagePickerController*) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
State = CAMERA_ACTIVITY_IMAGECAPTURED;
[[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (bool) IsAvailable
{
return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == YES;
}
- (bool) Show
{
if(![self IsAvailable]) return false;
State = CAMERA_ACTIVITY_WORKING;
UIImagePickerController* cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera; //Get Image From Camera
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects:(NSString*)kUTTypeImage, nil];
cameraUI.allowsEditing = YES;
cameraUI.delegate = self;
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentModalViewController:cameraUI animated:YES];
return true;
}
- (State) GetState
{ return State; }
#end
It looks like your not using your custom Camera class.
You create a class
UIImagePickerController* cameraUI = [[UIImagePickerController alloc] init];
set the delegate and some other items and display it to the user. But this is not an instance of a Camera class, it's an instance of UIImagePickerController. I believe you want to do
Camera* cameraUI = [[Camera alloc] init];
which will give you the implementation of your custom UIViewController however there is a another problem. Your Camera class is not a sub-class of UIImagePickerController, it is a sub-class of UIViewController so it will not be a UIImagePickerController. I think you intended it to be
#interface Camera : UIImagePickerController<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
On a side note, naming convention says only Class names should start with capital letters. Method names and variables should start with lower case letters.
Edit from comment:
Your Show method doesn't appeared to be called from anywhere, so I'm not sure how it would be used. More than that, looking at the method, I'm not sure how it would be used. The - at the beginning means it's an instance method which can only be called on an instance of Camera, but it is used to create and show an instance of Camera so the method requires and instance of an object to create and display a newly created instance of the object. Probably not what is intended. You could use a + to make it a class method and call it with [Camera Show], but you wouldn't be able to access instance variables because they would not exist.
Currently, I don't see anywhere where a Camera is created (outside Show) and could become the active view controller so it looks like you are just displaying a generic UIImagePickerController and not your custom class.
Related
I am trying to show a image picker view controller from a NSObject subclass but when i call presentViewController on the app's root vc the app crashes without a error message.
My code is here:
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
UIViewController *root = [[[[UIApplication sharedApplication] windows] objectAtIndex:0] rootViewController];
[root presentViewController:picker animated:YES completion:nil];
If i run this in a view controller it just works fine.
EDIT:
I can't use a delegate because i am building a plugin for Unity and i can't get the view controller in other ways than this.
I noticed the crash while calling this code in the current view controller from a IBAction:
- (IBAction)showPicker:(id)sender {
PhotoManager *manager = [[PhotoManager alloc] init];
[manager loadPhoto];
}
In answer to the comments here's the class code:
PhotoManager.h:
#interface PhotoManager : NSObject <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
- (void)loadPhoto;
#end
.m:
#implementation PhotoManager {
UIViewController *root;
}
- (void)loadPhoto {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
root = [[[[UIApplication sharedApplication] windows] objectAtIndex:0] rootViewController];
[root presentViewController:picker animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[root dismissViewControllerAnimated:YES completion:nil];
NSLog(#"%#", info);
}
#end
The proper way would be to create a protocol / delegate and call back to the presenting viewController telling it to present the picker. This allows you to do much more and is a great habit to learn right away IMO.
How do I set up a simple delegate to communicate between two view controllers?
I'm attempting to do a presentViewController with an UIImagePickerControl in order to display a standard Camera Roll photo picker. This works end-to-end in most of my app. The place where it does not work is when I want to use the imagePicker inside an already presented viewController; the view to present the album is unable to be presented.
The basic idea is I'm trying to either access the rootViewController on the window object, or the app delegate's persisted tabBarController (which is the rootViewController); both as examples of top level items that are hopefully always present. Just using "self" otherwise ends up as a partial view presenting it.
Is there a reliable way to presentViewController inside an already presentedView?
dispatch_async(dispatch_get_main_queue(), ^ {
// 1. attempt that works well elsewhere in app
[((AppDelegate*)[[UIApplication sharedApplication] delegate]).tabBarController presentViewController:self.imagePickerController animated:YES completion:nil];
// 2. this does nothing, no crash or action (_UIAlertShimPresentingViewController)
[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController:self.imagePickerController animated:YES completion:nil];
// 3. attempt off related internet suggestion, nothing happens
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
[topController presentViewController:self.imagePickerController animated:YES completion:nil];
});
Because presentViewController is meant as a modal presentation, I don't think it's possible to present a second modal at the same time in the same UIWindow. You could, however, add a new UIWindow and present it there.
originalWindow = [[[UIApplication sharedApplication] keyWindow];
tempWindow = [[UIWindow alloc] init];
UIViewController *controller = [[UIViewController alloc] init];
tempWindow.rootViewController = controller;
[tempWindow makeKeyAndVisible];
[controller presentViewController:self.imagePickerController animated:YES completion:nil];
You'll want to add something like this in your response to the image picker's return:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Process the result and then...
[self cleanupWindow];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self cleanupWindow];
}
- (void)cleanupWindow {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(madeKeyWindow:) name:UIWindowDidBecomeKeyNotification object:nil];
[originalWindow makeKeyAndVisible];
}
- (void)madeKeyWindow:(NSNotification *)notification {
[tempWindow removeFromSuperview];
tempWindow = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidBecomeKeyNotification object:nil];
}
i'm simply trying to show the camera or photo library so the user can select an image and return to the app. I could finally do it, but the problem that i'm facing, is that when the UIViewController ends (why the user selected an image or why the user pressed cancel) the app works, but events stopped working.
my UIViewController is defined like this:
#interface IOSNativeCb : UIViewController
- (void)imagePickerControllerUIImagePickerController *)picker didFinishPickingMediaWithInfoNSDictionary *)info;
#end
#implementation IOSNativeCb
- (void)imagePickerControllerUIImagePickerController *)picker didFinishPickingMediaWithInfoNSDictionary *)info {
[picker dismissModalViewControllerAnimated:YES];
[picker release];
//log all the dictionary of the selected image
for (id key in info) {
NSLog(#"key: %#, value: %# \n", key, [info objectForKey:key]);
}
}
//if user canceled
- (void)imagePickerControllerDidCancelUIImagePickerController *)picker {
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[picker dismissViewControllerAnimated:YES completion:^{[self dismissViewControllerAnimated:YES completion:nil];}];
[self removeFromParentViewController];
[window makeKeyAndVisible];
}
#end
and i'm initializing with this from openfl:
const void initAppGallery(){
UIWindow *window = [UIApplication sharedApplication].keyWindow;
IOSNativeCb *wn = [[IOSNativeCb alloc] init];
[window addSubview: wn.view];
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = wn;
[wn presentModalViewController:picker animated:YES];
[picker release];
}
tried several things in how i remove or dismiss the UIViewController to see if maybe the view still was principal and for that reason the events didn't worked anymore, but nothing so far.
any ideas of what i could try? anyone had any problem like this? is my first time coding in objetive-c + haxe, so i'm a bit lost of what functions or things could be the problem. i'm coding blind in a language i barely know.
Regards.
Some things that could help you find the error:
Use window.rootViewController = wn instead of [window addSubview:wn.view]
Don't release the picker after calling [picker dismissModalViewControllerAnimated:YES]. You already released the picker once on the initAppGallery method, so releasing it again could lead to unknown problems (probably crashes)
Calling [window makeKeyAndVisible] should be done at the end of initAppGallery.
Take also a look to this question, it might help you a bit.
the solution was more simple, i just added after [self removeFromParentViewController];:
[self.view removeFromSuperview];
and worked :D
In iOS 6, I was using the following code to push a UIImagePickerController, of source type UIImagePickerControllerSourceTypeCamera, and to show its navigation bar. I wanted to show the navigation bar because after taking the image, I'm pushing another VC that allows the user to set some attributes in the database.
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
cameraController = [[UIImagePickerController alloc] init];
cameraController.delegate = self;
cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:cameraController animated:YES completion:NULL];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
cameraController.topViewController.title = #"Add";
cameraController.navigationBar.translucent = NO;
cameraController.navigationBar.barStyle = UIBarStyleDefault;
[cameraController setNavigationBarHidden:NO animated:NO];
}
In iOS 7 this code no longer shows the navigation bar. Does anyone know if there's a way to to get the navigation bar back for UIImagePickerController, of source type UIImagePickerControllerSourceTypeCamera?
Guess what? When imagePicker presents, it's automatic set to hidden....
All you need to do is setHidden:NO in next runloop. Like:
[self presentModalViewController:imagePicker animated:YES];
[self performSelector:#selector(showNavigationBar:) withObject:imagePicker afterDelay:0];
- (void)showNavigationBar:(UIImagePickerController*)imagePicker {
[imagePicker setNavigationBarHidden:NO];
}
#LeverkusenFan's solution works well. But instead of using a hack such as a run loop, you use the completion handler of presentViewController to achieve that effect.
[self presentViewController:cameraController animated:YES completion:^{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
cameraController.topViewController.title = #"Add";
cameraController.navigationBar.translucent = NO;
cameraController.navigationBar.barStyle = UIBarStyleDefault;
[cameraController setNavigationBarHidden:NO animated:NO];
}];
In fact a better solution that avoids the weird animation when the navigation bar shows up and which works well when you press the back button on the nav bar is as follows:
In the delegate for the UIImagePickerController implement the following function.
- (void) navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (navigationController == self.cameraController && navigationController.viewControllers.count == 1) {
// When showing the ImagePicker update the status bar and nav bar properties.
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
navigationController.topViewController.title = self.cameraTitle;
navigationController.navigationBar.translucent = NO;
navigationController.navigationBar.barStyle = UIBarStyleDefault;
[navigationController setNavigationBarHidden:NO animated:animated];
}
}
This function will get called when the ImagePicker is shown and we only make the changes for the rootViewController of the ImagePicker (i.e. the camera screen).
I am using this code to push a view controller in ViewControllerA:
DatePickerViewController *viewController = [[DatePickerViewController alloc] initWithNibName:#"DatePickerViewController" bundle:nil];
NSMutableArray *info = [[NSMutableArray alloc] initWithObjects:#"Exam Duration",nil];
[viewController setInfo:info];
[info release];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
In the DatePickerViewController that is slid in, it has a text field. In ViewControllerA how can I get the value of this text field once the viewWillDisappear method is fired, it's to save data.
There are several ways to do this. Best would be to use a delegate pattern or a notification system.
Which one you choose depends on your whishes. If you just want the 'parent controller' to know about the value, use a delegate method.
Using delegates or notifications creates a loosely coupled structure and makes reusable classes possible.
EDIT: Some sample code. Beware: untested and from the top of my head!
DatePickerViewController.h
#protocol DatePickerViewControllerDelegate
-(void)datePicker:(DatePickerViewController *)picker pickedDate:(NSDate *)date;
#end
DatePickerViewController.m
- (void)viewWillDisappear:(BOOL)animated {
[self.delegate datePicker:self pickedDate:theDate]; //Delegate is an ivar
[super viewWillDisappear:animated];
}
ViewControllerA.m
//
DatePickerViewController *viewController = [[DatePickerViewController alloc] initWithNibName:#"DatePickerViewController" bundle:nil];
NSMutableArray *info = [[NSMutableArray alloc] initWithObjects:#"Exam Duration",nil];
[viewController setInfo:info];
[info release];
[viewController setDelegate:self]; // Set the delegate of the date picker
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
//
-(void)datePicker:(DatePickerViewController *)picker pickedDate:(NSDate *)date; {
//Deal with the new date here
}