iOS show view controller from NSObject not working - ios

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?

Related

presentViewController didnt work appdelegate

line presentViewController crash app
#interface VVPassbook : NSObject
#property (nonatomic, retain) id delegate;
-(void)gotPassbookFromUrl:(NSURL *)passbookUrl;
#end
#import "VVPassbook.h"
#implementation VVPassbook
-(void)gotPassbookFromUrl:(NSURL *)passbookUrl
{
//present view controller to add the pass to the library
PKAddPassesViewController *vc = [[PKAddPassesViewController alloc] initWithPass:pass];
[vc setDelegate:(id)self.delegate];
[self.delegate presentViewController:vc animated:YES completion:nil];
}
#end
im call this method in AppDelegate.m in method
- (void)application:(UIApplication*)application didReceiveRemoteNotification:
(NSDictionary*)userInfo
{
VVPassbook *pass = [[VVPassbook alloc] init];
pass.delegate = self.window.rootViewController;
[pass gotPassbookFromUrl:MYURL ];
}
all times error
reason: 'Application tried to present a nil modal view controller on target <VVRightManuViewController: 0x15dd28460>.'
you should present a viewController from a UIViewController, i.e., self
(if self is a viewcontroller).
Note that you will get a "Attempt to present on whose view is not in the window hierarchy"-warning when self (this viewcontroller) is detached from the Main Window.
Solution: always use addChildViewController: to child viewcontrollers.
Why are you using self.delegate to push the view Controller,
simple write
[self presentViewController:vc animated:YES completion:nil];
in the place of
[self.delegate presentViewController:vc animated:YES completion:nil];

presentViewController-- Attempt to present __ on __ whose view is not in the window hierarchy

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];
}

Main app stop responding to events after UIViewController ends

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

Variables not kept when changed in a callback

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.

How to access the parent view controller?

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
}

Resources