Cannot click back button when open view from remote notirication - ios

My iOS application using Objective C. It received a remote notification, when a user click it, it will open a particular view.
This view has a back button in the navigation tab. In normal scenarios can back to root view. However, when I open this view from a notification, cannot back to any view nor leaving the application.
Here is my spruce code:
1. from AppDelegate.m to open a particular view:
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *viewAnnouncement =[storyboard instantiateViewControllerWithIdentifier:#"Announcement"];
self.window.rootViewController = viewAnnouncement;
[self.window makeKeyAndVisible];
Action function to back to previous view (in viewClass.m):
- (IBAction)onBtnBackClicked:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
I believe the problem is, when I open this view from a notification, no parent view to let it back to. Can anyone help me, when user open this view from a notification, whether it goes to a root view or back to the previous opening app

EDIT : The previous answer didn't take in count the fact that you were in AppDelegate. Maybe this answer is better.
Turn your [self dismissViewControllerAnimated:YES completion:nil]
into :
UINavigationController *navigationController = [[UINavigationController alloc] init];
UIViewController *yourPreviousVC = [[UIViewController alloc] init];
[navigationController pushViewController:yourPreviousVC animated:YES];
If I'm wrong or if I missunderstood the question, do not hesitate to correct my answer.

Try this
Announcement *annouce = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"Announcement"];
HomeVC *home = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"HomeVC"];
[((UINavigationController *)self.window.rootViewController) setViewControllers:#[home,annouce] animated:YES];
And If app already open and you want to go previous page
Announcement *annouce = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"Announcement"];
UIViewController *visibleView = ((UINavigationController *)window.rootViewController).visibleViewController;
[((UINavigationController *)self.window.rootViewController) setViewControllers:#[visibleView,annouce] animated:YES];

I found the solution. My app has multiple views which I can go from a navigation list. When I want to back to main view, I just dismiss the current view.
Cuurently, a view is opened when I click a notification and I can't simply dismiss this view, Coz it considered as root view.
What I did to solve it, I connect my view (Announcement) by segue to main view, and set an identifier (for example: "leaveAnnouncementPage". Then, in your class just call this function in back button's fnction to back to the main view:
[self performSegueWithIdentifier:#"leaveAnnouncementPage" sender:self];
Here is also some useful links:
First
Second
Thank you guys for your help, and all the best

Related

How to skip the root view and go directly to displaying the second view in a UINavigationController hierarchy?

I am making a messaging app on ios that will have multiple folders for different types of messages. I will be using a navigation controller structure and would like the root view to be where the user can choose which folder to view. However, when I first segue to the navigation controller I would like the inbox folder view to display directly (ie. bypass the root view). Apples mail app has a similar structure (launches inbox when it opens). how can I do this?
It really depends on how the relationship is between the first view controller and the second view controller. If you want to do something like this, why don't you put your second view controller as the root controller of the UINavigationController.
Anyway, if you still want to do it the way you describe, you can just direct to the second view controller using the viewDidLoad method from your root view controller. But, it will make the UI looks clumsy.
try to use this code :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UIStoryboard *board = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController *controller1 = [board instantiateViewControllerWithIdentifier:#"firstView"];
UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:controller1];
SecondViewController *secondView = [board instantiateViewControllerWithIdentifier:#"secondView"];
[controller1 addChildViewController:secondView];
[self.window setRootViewController:navController];
return YES;
}
Hope this will help you.
Write this code in didFinishLaunchingWithOptions of AppDelegate.m
UIStoryboard *MainStoryboard = [UIStoryboard storyboardWithName:#"Main"
bundle: nil];
UINavigationController *controller = (UINavigationController*)[MainStoryboard
instantiateViewControllerWithIdentifier: #"YourStoryBoardID"];
NeededViewController *need=[MainStoryboard instantiateViewControllerWithIdentifier:#"YourStoryboardID"];
[controller setViewControllers:[NSArray arrayWithObject:need] animated:YES];
self.window.rootViewController=controller;

Confused about storyboards and pushing programmatically ios Objective C

I am really confused about the relationship between storyboards and pushing to views programmatically.
I am using SWRevealViewController to display a menu of items.
If I push to the storyboard using
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
PhotosViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"PhotosViewController"];
[self presentModalViewController:controller animated:YES];
[self.navigationController pushViewController:controller animated:YES];
All of the information in my storyboard is displayed but there is no "back" button to the SWRevealViewController.
If I push to the view controller using
PhotosViewController *frontViewController = [[StreamScreen alloc] init];
newFrontController = [[UINavigationController alloc] initWithRootViewController:frontViewController];
Then I can view everything that I have added programmatically but nothing from the storyboard.
My question is how can I access things from both storyboard and things Ive added programmatically.
if you present the view controller then it will not give you default back button because when you present a controller it will not added in navigation stack of NavigationController so it won't give you that option.
If you want push controller do not use presentModalViewController.
Try like below
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
PhotosViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"PhotosViewController"];
[self.navigationController pushViewController:controller animated:YES];
and if you want to present controller then create manually add a back button like we have default in navigation back button and on it's click write below code to dismiss the controller.
[self dismissViewControllerAnimated:YES];
Hope this helps you.

Storyboard - Workflow for turn based game

I am creating a turn based game and would like to know the correct process for my workflow. At present I have the following:
Home View Controller (which has a UITableView)
Click on row from section 1 > Loads a UINavigationController with path 1
Click on row from section 2 > Loads a UINavigationController with path 2
As an example:
path 1 - play your turn
path 2 - guess your turn
Each path has around 4-5 UIViewControllers loaded into the navigation controller.
Now I am at the stage where once path 2 is complete the user should then play their turn too (ie take path 2 then path 1).
What is the correct way to complete this? should I create a segue from the last controller in path 2 > leading to path 1. The issue is that path 2 has a UIViewController that has a UIImageView with a large image in and it would hang around in memory. Ideally it is cleared as such, before the user starts path 1 (after path 2 is complete)
I have tried popToRootViewControllerAnimated but its not working when we want movetopath2.
We can store some checkpoints in NSUserDefaults and then segue accordingly but that approach doesn't work in this case.
[self.navigationController popToRootViewControllerAnimated:YES];
Finally I have found solution as follows:
Start with below working code and change it as per your app logic
ViewController.m File
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.section == 0)
{
UIStoryboard *mainStoryBoard = [UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]];
PathOneViewController *pathController = [mainStoryBoard instantiateViewControllerWithIdentifier:#"PathOneViewController"];
[self.navigationController pushViewController:pathController animated:YES];
}
else
{
UIStoryboard *mainStoryBoard = [UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]];
PathTwoViewController *pathController = [mainStoryBoard instantiateViewControllerWithIdentifier:#"PathTwoViewController"];
[self.navigationController pushViewController:pathController animated:YES];
}
}
PathOneDetailViewController.m File
- (IBAction)actionMoveToPathTwo:(id)sender { // Move to path two
AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];
UIViewController* rootController = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"PathTwoViewController"];
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
appDelegateTemp.window.rootViewController = navigation;
}
- (IBAction)actionMoveToHome:(id)sender { // Move to table View
AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];
UIViewController* rootController = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"ViewController"];
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
appDelegateTemp.window.rootViewController = navigation;
}
PathTwoDetailViewController.m File
- (IBAction)actionMoveToHome:(id)sender { // Move to table View
AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];
UIViewController* rootController = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"ViewController"];
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
appDelegateTemp.window.rootViewController = navigation;
}
- (IBAction)actionMoveToPath1:(id)sender { // Move to path one
AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];
UIViewController* rootController = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"PathOneViewController"];
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
appDelegateTemp.window.rootViewController = navigation;
}
Fire a notification from path 2
Be sure you subscribe to that notification in Home View Controller
In the notification handler method in Home View Controller, call the
current navigation controller's popToRootViewControllerAnimated:.
Manually call the segue to start path 1.
The answer that worked for me was actually a much more simple approach. Adding a ViewController reference to the starting view controller for Path2. Then simply changing the view controllers in the UINavigationController stack. As the navigation controller is not subclassed or doesn't need to be this works fine.
- (IBAction)completeButtonPressed:(id)sender {
NSLog(#"complete button pressed");
Path2ViewController *path2StartVC = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"Path21VC"];
[self.navigationController setViewControllers:[NSArray arrayWithObject:path2StartVC] animated:YES];
}
I will drop a sample project onto github for reference shortly.
I would suggest you set your first controller as a UINavigationController and set its root control to the "Home View Controller" (UITableView). I'll call this the root navigation controller for now on. You then can keep the rest of the app's structure the same as you have it now, with one UINavigation controller for each route.
This would give you the advantage of being able to call the root navigation controller popToRootViewControllerAnimated:. Which in this case would be your "Home View Controller". There is one slight disadvantage to this and that is with your paths you can't just call popToRootViewControllerAnimated: on the current navigation controller when on a path. Since this would return you to the beginning of the path that you are on.
But this is easily fixed by keeping a reference to the root navigation controller. Which I would do by subclassing UINavigationController and just adding a property that stores a reference to the root view controller. The assignment would need to be done while the view controllers are transitioning from the table view to the navigation controllers that control one of your two paths. I think prepareForSegue:sender: would be your best bet to handle this.
As for your worry of the UIImageView staying in memory. You do not need to worry about it. When the view controller unloads it will unload the UIImageView as well. The only way you could keep a view controller from unloading is keeping a strong reference to it in another view controller. Which sometimes you want eg. parent / child relationship sometimes you don't eg. segue between view controllers. But passing properties between view controllers should be fine. For more info check out View Controller Guide
There are potentially a ton of different ways to solve this question. So, I would take my answer as a starting point and adapt it to what works best for the future of your app and your code style.

Attempt to present a viewcontroller whose view is not in the window hierarchy

I have a tab bar application. My requirement is I select the 'scan' tab to scan the qr code and navigate/jump immediatley to another 'list' tab. Both 'scan' and 'list' tab are there in the viewControllers array in didFinishLaunchingWithOptions After referring this link, i don't think i need to set the delegate as both the tabs are already present in the hierarchy.
I get this warning in the following line
if(x)
{
listViewCntrl = [[ListViewController alloc] initWithNibName:#"ListViewController" bundle:nil];
listViewCntrl.getFlag = YES;
[self presentViewController:listViewCntrl animated:YES completion:Nil]; // I get the warning here
}
If I comment out the above code and add
[self.tabBarController setSelectedIndex:1];
then I would not be able to get the subView of the listViewController (set flag to show the subview) which i need to display inside the list tab after scanning.
App crashes if I add
[self.tabBarController setSelectedViewController:listViewCntrl];
So how do I display the listView's subview after scanning?
You can try this if you use storyboard:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController * destViewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewControllerIdentifier"];
[self.navigationController pushViewController:destViewController animated:YES];
You have to set an identifier for your controller in your storyboard.
ListViewController *listController = (ListViewController*)[self.tabController viewControllers][1];
listController.getFlag = 1;
[self.tabBarController setSelectedIndex:1];
The problem is that you're creating an entirely new ListViewController. You say you already have one in the tab controller - you don't need a new one.
You can't use your 3rd option because, again, the two ListViewController's are different objects (they may be of the same class, but they point to a different address).

Linking a new viewcontroller to Storyboard?

There is probably a simple solution but I can't figure it out.
I am using storyboards for the interface.
I start with a tab bar controller, but before the user is allowed to use the app the user has to authenticate himself trough a loginview which is modally pushed at the start.
I want to configure the loginview at the same storyboard, but I can't seam to figure out how to link the view controller at the storyboard and my code.
What I have done:
Create a new UIViewController subclass trough file > new > new file.
Drag a new UIViewController in the story board
Set the class in the custom class tab
drags a UILabel for test purpose.
run
No label...
Pull on a new UIViewController that will act as the login view controller onto the MainStoryboard. In the attribute inspector change the identifier to LoginViewController (or something appropriate). Then add
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[vc setModalPresentationStyle:UIModalPresentationFullScreen];
[self presentModalViewController:vc animated:YES];
}
to the First view controller and the login screen will be loaded from your storyboard and presented.
Hope this helps.
The answer by Scott Sherwood above is most correct answer I found after lot of searching. Though very slight change as per new SDK (6.1), presentModalViewController shows deprecated.
Here is very small change to above answer.
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
HomeViewController * hvc = [sb instantiateViewControllerWithIdentifier:#"LoginView"];
[hvc setModalPresentationStyle:UIModalPresentationFullScreen];
[self presentViewController:hvc animated:YES completion:nil];
I'm new in this field. But if the first view controller is a navigation view controller and its rootviewcontroller is a table view controller. If you want to push a view controller like the LoginViewController when you click the cell, and you also want to go back to the table view by using the navigation bar. I recommend this way:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *controller = [sb instantiateViewControllerWithIdentifier:#"LoginViewController"];
[self.navigationController pushViewController:controller animated:YES];
}
In this way, you can have the navigation.
By the way, I don't know why this kind of problem you asked will appear. I guess when the loginviewcontroller is created in the code, its view is not the view in the storyboard. If someone know the cause, please tell me! thanks!

Resources