This is going to take some explaining... So I'll do it in order:
I have a navigation controller where the rootViewController is called TipsCollectionViewController.
I have a UserViewController that's loaded as a popup:
UserViewController * userView = [[UserViewController alloc] initWithNibName:#"UserView" bundle:[NSBundle mainBundle]];
[userView setUEmail:email];
[self presentPopupViewController:userView animationType:MJPopupViewAnimationSlideTopBottom];
I then have another popup that loads on top of THAT popup:
Place *p = [placeArray objectAtIndex:indexPath.row];
DetailPlaceViewController *pvc = [[DetailPlaceViewController alloc] init];
[pvc setPlace:p];
NSLog(#"%#", p.PName);
[self presentPopupViewController:pvc animationType:MJPopupViewAnimationSlideTopBottom];
Now there's a reason I've done this: the AppDelegate features a Navigation controller and previously I loaded the UserView like this:
[self.navigationController presentViewController:userView animated:YES completion:nil];
But this meant that the UserView would load on the iPhone but not on the iPad for some odd reason. But when I switched it to the popup view it worked fine.
So now I load both the UserView and the DetailPlaceView in a popup... but now it CLOSES on the iPad but not on the iPhone.
Here's the code for closing the detail view:
- (void) didTapBackButton:(id)sender {
NSLog(#"View Controller Number: %lu", (unsigned long)self.navigationController.viewControllers.count);
if(self.navigationController.viewControllers.count > 1) {
[self.navigationController popViewControllerAnimated:YES];
NSArray *stack = self.navigationController.viewControllers;
TipCollectionViewController *tipsVC = stack[stack.count-1];
[tipsVC.collectionView reloadData];
}
I know there's a better way to handle this whole thing... but what should I be doing differently?
UPDATE
If I switch it back to:
[self.navigationController pushViewController:userview animated:YES];
...for the UserView on the iPhone and:
[self.navigationController pushViewController:pvc animated:YES];
...for the view that loads after that, then the UserView will load... but the next viewcontroller (the DetailPlaceViewController) won't load. I think that's my main problem. I could probably dismiss the second view controller at that point if I could get it to load. Any ideas?
[self presentPopupViewController:pvc animationType:MJPopupViewAnimationSlideTopBottom];
if you are using this,presentPopupViewController is used to display like modal view which cant be dismissed without any custom cancellation action.
To use popOverViewController method, use pushViewController. Then that one you can dismiss by this technique.
Ultimately all I had to do was switch:
[self.navigationController pushViewController:pvc animated:YES];
to:
[self presentViewController:pvc animated:YES];
And it works. It opens and closes the view and everything works as it should.
I'm getting a warning on the DetailView once you click an item:
Presenting view controllers on detached view controllers is discouraged <UserViewController: 0x16e02020>
So I'll wait a couple of days to accept my answer. Otherwise, despite the warning, it seems to be working.
Related
I have the whole app embedded in UINavigationController. Now there is the Home Screen that has several modules for user to choose. Now, when the user clicks on the module it is NAVIGATED and if user desires to choose some another module from any other modules available, there is a button in navigation bar, which PRESENTS the HomeViewController modally on top of the current module and then user can choose any module from there which will NOT BE PRESENTED instead they will NAVIGATE.
Now what I have done is made a delegate called navigate on HomeViewController and will be override by viewcontrollers of each module and it will take the reference of the new ViewController with it. Then when this method is called I have first dismissViewController the HomeViewController and then navigated to the new ViewController that I have the reference.
Now, what the real issue is that SOMETIMES there is a jerk when navigating from one module to other and sometime it works fine. That why I am not able to debug also. The jerk is that when a module is clicked from HomeViewController, the home screen disappears and the appears again and then it actually navigates.
The code for navigating to a module from HomeViewController is
RadiusSearchViewController *rad = [self.storyboard instantiateViewControllerWithIdentifier:#"RadiusSearchViewController"];
[self.delegate navigate:rad];
This navigation overrided method in all modules is
-(void)navigate:(UIViewController*)uiViewController{
NSLog(#"inside navigate method");
[self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
[self.navigationController pushViewController:uiViewController animated:YES];
}
I assure you that it is coming in this method.
Now the code that presents the HomeViewController modally is
ViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"HomeVC"];
vc.view.backgroundColor = [[UIColor whiteColor]colorWithAlphaComponent:0];
vc.delegate = self;
vc.providesPresentationContextTransitionStyle = YES;
vc.definesPresentationContext = YES;
vc.modalPresentationStyle = UIModalPresentationOverCurrentContext;
vc.fromOutside = true;
NSLog(#"Presneting...");
[self presentViewController:vc animated:NO completion:nil];
The reason for PRESENTING and NOT NAVIGATING the HomeViewController is that it comes on the top of the current module in transparent form which is necessary.
REMEMBER: It happens sometimes not all of the time. Like you can say half of the times.
[self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
[self.navigationController pushViewController:uiViewController animated:YES];
this should be changed to...
[self.presentedViewController dismissViewControllerAnimated:YES
completion:^{
[self.navigationController pushViewController:uiViewController animated:YES];
}];
this will make sure navigation view controller will be called after dismiss is done. check and see if it solves your problem.
I have 3 different viewControllers. first one is the main page where you will see many thumbnail of images. Second controller will only open when an image is clicked. Last ViewController is the login which opens when the user is not logged in but trying to view the second controller or clicked login from the main controller. In the login controller I have a back button to go to main page. Remember the user can access login from two different ways. by clicking on the image or clicking login in the main controller directly. So the issue is the back button does work properly. When the login page is accessed from the main controller directly, then the back button works fine it takes you to the main page. However, if you click on any of the thumbnails the second controller will try to open but since your not logged in the login page will show and when you click the back button to send you to the main controller, it won't go to main instead it will reload the same page and you kind of stock in a loop you have to login or exit the app. any ideas why?
The First ViewController Identifier is set to "mainImages".
- (IBAction)backToMain:(id)sender {
[self dismissViewControllerAnimated:NO completion:nil];
NSString * storyboardName = #"MainStoryboard";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController * MI = [storyboard instantiateViewControllerWithIdentifier:#"mainImages"];
UINavigationController *navController = self.navigationController;
if (navController) {
[navController pushViewController:MI animated:NO];
}
}
I also tried this but no luck:
[self dismissViewControllerAnimated:NO completion:nil];
UIViewController * MI = [self.storyboard instantiateViewControllerWithIdentifier:#"mainImages"];
[self presentViewController:MI animated:NO completion:nil];
Try this if you are presenting as modal:
- (IBAction)backToMain:(id)sender {
[self dismissViewControllerAnimated:NO completion:^{
UIWindow *window = [[UIApplication sharedApplication].windows firstObject];
UINavigationController *nav = (UINavigationController *)window.rootViewController;
[nav popToRootViewControllerAnimated:YES];
}];
}
If you need to "go back" from the login view to any other view (in this case 2 of them), you should try using a Modal Segue, a modal segue lose the navigation controller view stack but allow you to dismiss the login view and go back to you last view.
On the first (or thumbnail images view controller) show your new modal view as this:
UIViewController *modalViewController = [[UIViewController alloc] init];
[self presentViewController:modalViewController animated:YES completion:nil];
And then, to dismiss (on login view controller):
- (IBAction)backToMain:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
EDIT
After some more digging around it has become apparent that if i try to call a segue from the view controller class after a fast application switch to/from Safari i get an exception stating that the segue does not exist. It definitely does, in the VC that is loaded on screen, and i have checked the spelling and it's 100% correct.
What makes it even weirder is that if i call that exact same method from an IBAction it works fine.
Here's the code i am using:
-(void)goToPhotoPage{
[self performSegueWithIdentifier:#"twitterAuthComplete" sender:self];
}
If i call this when the app resumes i get an exception, however if i call it from this IBAction, attached to a UIButton, with doing nothing different the segue works as expected and no exception.
- (IBAction)twitterToPhoto:(id)sender
{
[self goToPhotoPage];
}
Arrrrgrghrhhgrgrg!
ORIGINAL QUESTION
I am working on an iPad application that uploads user photos to Facebook/Twitter.
As part of the process i have to jump to Safari to do OAuth for twitter, the app then jumps back via a specific URL and get's the tokens e.t.c
However for some reason when the application re-awakes i cannot trigger any segue's or manually load any VC's.
I have a success block in my AppDelegae which get's called when the upload is complete which calls a method in the VC to close a spinner and segue to the success view.
The spinner stops as expected, but no matter what i try i cannot get the segue to work.
Everything is hooked up, the segue has an ID and i get no error or exceptions in the console, just nothing happens. The only way i can get code triggered segue to work after this point is to use a user trigger one connect to a UIButton, after that one complete they start to work again.
Here is the code with callback in my AppDelegate:
- (void)postToTwitter:(NSString*)postText image:(UIImage*)image
{
NSData *data = UIImageJPEGRepresentation(image, 0.5);
[_twitter postStatusUpdate:postText
mediaDataArray:#[data]
possiblySensitive:nil
inReplyToStatusID:nil
latitude:nil
longitude:nil
placeID:nil
displayCoordinates:nil
uploadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
} successBlock:^(NSDictionary *status) {
ViewController * vc = [[ViewController alloc]init];
[vc triggerSuccess];
} errorBlock:^(NSError *error) {
ViewController * vc = [[ViewController alloc]init];
[vc uploadFail:error];
}];
}
in the VC i have tried the following:
- (void)triggerSuccess
{
[self segueToSuccess];
}
- (void)segueToSuccess
{
[self hideMessage];
[self.navigationController performSegueWithIdentifier:#"approveSegue" sender:self];
}
As well as:
- (void)triggerSuccess
{
[self segueToSuccess];
}
- (void)segueToSuccess
{
[self hideMessage];
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"done"];
[self.navigationController pushViewController:controller animated:YES];
}
In a moment of pure desperation i even tried:
- (void)triggerSuccess
{
[self segueToSuccess];
}
- (void)segueToSuccess
{
[self hideMessage];
//[self.navigationController performSegueWithIdentifier:#"approveSegue" sender:self];
[self dismissViewControllerAnimated:NO completion:nil];
[self.navigationController popToRootViewControllerAnimated:NO];
[self performSelector:#selector(segueToSuccessPart2) withObject:nil afterDelay:0.25];
}
- (void)segueToSuccessPart2
{
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"done"];
[self.navigationController pushViewController:controller animated:YES];
}
I've obviously missed something, and i'm guessing it's to do with the application going into the background and the VC being "unloaded" from memory, and needing re-instatiating, but i'm not sure where to begin with that.
Any help would be greatly appreciated as i'm about to throw the computer out of the window . .
Thanks
Gareth
Edit 1
After doing some more troubleshooting it appears that during this call in the VC:
[self dismissViewControllerAnimated:NO completion:nil];
[self.navigationController popToRootViewControllerAnimated:NO];
self.navigationController is nil . . .
I'm guessing that is likely the cause of the issues i'm seeing?
Edit 2
So as suggested by Kelin, i am trying to make sure i retain the Navigation Controller.
I have done the following in AppDelegte:
#property (nonatomic, strong) UINavigationController *navController;
and
#synthesize navController;
But when i try to access this from the VC using the below code, it's still nil . .
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
UINavigationController *navController = appDelegate.navController;
[self dismissViewControllerAnimated:NO completion:nil];
[navController popToRootViewControllerAnimated:NO];
Edit 3
I have also added this to the AppDelegate, which means the Navigation Controller is no longer nil. But still nothing happens . . .
if (!self.navController)
{
self.navController = [[UINavigationController alloc]initWithNibName:#"Main" bundle:nil];
}
The problem is you trying to perform segue from storyboard using programmatically created View Controller.
You use -performSegueWithIdentifier: sender: completely wrong. In this method "sender" is a button, or any other control which action initialises segue, but not the view controller to be pushed.
Usually segue is created with storyboard. But if you really need to create it manually, call:
-[UIStoryboardSegue initWithIdentifier:source:destination:]
where source and destination are view controllers.
Read this: https://developer.apple.com/library/ios/recipes/xcode_help-interface_builder/articles-storyboard/StoryboardSegue.html
or this (about custom segues):
You can not do both dismiss and pop together either you have to pop or dismiss the view controller and
Every ViewController have it's own NavigationController which is default point out to navigation controller you added to AppDelegate.
So, You also can access it by self.navigationController (Edit 3)
Also check whether you initialised navigation controller in
AppDelegate
self.navController = [[UINavigationController alloc] initWithRootViewController:firstVC];
I use the following code to switch from main view (ViewController.h)to another view (TouchViewController) and then similarly switch from this view to the next view (QuestionsViewController).
How do I go back to the main view from here? i.e what is the value for initWithNibName for MainStoryboard.storyboard?
- (IBAction)startGame:(UIButton *)sender {
TouchViewController *second = [[TouchViewController alloc] initWithNibName:#"TouchViewController" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
and
- (IBAction)selectTopic:(UIButton *)sender {
NSString *category = [[sender titleLabel] text];
[[NSUserDefaults standardUserDefaults] setObject:category forKey:#"MyKey"];
QuestionsViewController *second = [[QuestionsViewController alloc] initWithNibName:#"QuestionsViewController" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
Now to go back to the main view i have a problem i.e
- (IBAction)backtoMainMenu:(id)sender {
ViewController *second = [[ViewController alloc]initWithNibName:#"MainStoryboard.storyboard" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
At this point the app just crashes! I suppose it's because initWithNibName:#"MainStoryboard.storyboard" can't be used but the ViewController does not have any other xib file - so what do I do?
First of all storyboard can't be used as a xib file they are not the same thing. Second you have one view controller that presents a view controller that also present a view controller so what you can do is something like this from the third view controller:
[self.presentingViewController.presentingViewController
void)dismissViewControllerAnimated:YES completion:nil];
This is the fastest way you can do it, but it's not really reusable, if you have a more complex view controller hierarchy than it will be really ugly to do it like this, other solutions can be delgates or notifications.
Also just a piece of advice, when you want to go back to a previous screen in general you don't what to create a new view controller (as you try in backToMainMenu method, you simply want to remove all the view controllers (screens) until you reach the desired screen.
I have a help screen that can be accessed from any other screen. The only button there is a "back" arrow. Right now it always goes back to the main screen. However, I need it to go back to which ever screen that was before it. I've hunted through stackoverflow and google but no luck.
EDIT
snippet of the code I'm using now:
appViewController *homeView = [[appViewController alloc] initWithNibName:nil bundle:nil];
homeView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:homeView animated:YES];
From the modal view controller that is being presented right now, make it dismiss itself when clicking the home button:
- (void)buttonClicked
{
[self dismissModalViewControllerAnimated:YES];
}
On button click You can use.
[self.navigationController popViewControllerAnimated:YES];
Maybe this link will help you: How to navigate previous screen from camera view in iphone?
Its about navigation and how to remove the last screen.
Or use this in the click event of button.Create an instance of the view controller to be opened
EnterNameController *newEnterNameController = [[EnterNameController alloc] initWithNibName:#"EnterName" bundle:[NSBundle mainBundle]];
[[self navigationController] pushViewController:newEnterNameController animated:YES];