I have a UITableViewController embedded in a UINavigationController.
When I click a button in my Detail View (Detail view is in a UITabViewController) to showList the UITableViewController (List Table View) called List is presented.
If I click the Back button in the navigation controller, I am taken back the correct screen: Detail View Shown by the Blue Line
If I select an list item the delegate method didSelectFromList is called from the didSelectRowAtIndexPath and the app is taken back to main screen (Home View) shown by
the red line.
What I want is the purple line.
Any help?
Attempt 1:
I let the delegate dismiss the List TableVC.
#import "ListTableViewController.h"
#interface CallDetailViewController () <ListTableViewControllerDelegate>
#property (nonatomic, strong) LotListTableViewController * lltvc;
#end
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"segue_list"]) {
self.lltvc = (ListTableViewController*)segue.destinationViewController;
self.lltvc.delegate = self;
}
}
- (void) showList {
[self performSegueWithIdentifier:#"segue_list" sender:self];
}
#pragma mark LIST SELECTION DELEGATE METHOD
- (void) didSelectFromList:(NSString *)item {
[self.lltvc dismissViewControllerAnimated:YES completion:^{
NSLog(#"Did Select item: %#", item);
}];
}
In the LIST TABLE VIEW CONTROLLER
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
///.. stuff to find the correct string
[self.delegate didSelectFromList:item];
}
Attempt 2:
Here I let the List TableVC dimiss itself.
#import "ListTableViewController.h"
#interface CallDetailViewController () <ListTableViewControllerDelegate>
#end
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"segue_lots"]) {
NSLog(#"GOING TO THE LOT LIST");
UINavigationController * nvc = (UINavigationController*)segue.destinationViewController;
LotListTableViewController *lltvc = [nvc childViewControllers][0];
lltvc.delegate = self;
}
}
- (void) showLotList {
[self performSegueWithIdentifier:#"segue_lots" sender:self];
}
#pragma mark LIST SELECTION DELEGATE METHOD
- (void) didSelectFromList:(NSString *)item {
NSLog(#"Did Select item: %#", item);
}
In the LIST TABLE VIEW CONTROLLER
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
///.. stuff to find the correct string
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate didSelectFromList:item];
}];
}
I have also tried from the didSelectRowAtIndexPath in the List TableVC
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self.delegate didSelectFromList:item];
}];
If you keep on pushing view controller, they get added to the navigation stack, so if you do [self dismiss or [self.navigationController dismiss, all the navigation stack is cleared and you will betaken to the rootViewController.
Either you can pop one view controller or
Instead of pushing the navigation controller from detailVC, present the navigation controller that has the list table VC. In that case [self.nav dismissviewCOntroller will work,
Related
I have a view controller that has two table view controllers as subviews.
When I click one of the cells in the table view controller, I would like it to push to a new view controller. However, it says self.navigationController and self.parentViewController.navigationController are (null).
Does anyone know how I can push a new view from a subview? Thanks!
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ProductClass *productClass = [arrProducts objectAtIndex:indexPath.row];
ProductSingleViewController *productSingleViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ProductSingleViewController"];
productSingleViewController.prod_title = productClass.title;
NSLog(#"VC1: %#, VC2: %#", self.navigationController, self.parentViewController.navigationController);
[self.parentViewController.navigationController pushViewController:productSingleViewController animated:YES];
}
One way to handle this is by setting a delegate method inside the UITableViewControllers. Add the delegate method to your parent view controller. It will get triggered whenever someone taps a cell inside the tables. From this delegate method you will be able to push a new view controller onto the stack.
In MyTableViewController.h:
#protocol MyTableDelegate <NSObject>
#required
- (void)selectedCellWithInfo:(NSDictionary *)info
#end
#interface MyTableViewController : UITableViewController {
id <MyTableDelegate> delegate;
}
#property (strong) id delegate;
In MyTableViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[delegate selectedCellWithInfo:[myTableDataSource[indexPath.row]];
}
In MyMainViewController.h:
#import "MyTableViewController.h"
#interface MyMainViewController : UIViewController <MyTableDelegate>
In MyMainViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
myTableViewController.delegate = self;
}
- (void)selectedCellWithInfo:(NSDictionary *)info {
// Do something
}
The navigationController property of a view controller will return a valid navigation controller object only if the view controller is in a navigation controller's navigation stack.
If the PRoductSingleViewController is your rootviewcontroller
1. Drag and drop the navigation controller
2. set the navigation controller as rootviewcontroller.
3. Replace the navigation child Viewcontroller with your PRoductSingleViewController
Or you can manage it in app delegate.m class
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_productSingleViewController = [[PRoductSingleViewController alloc]initWithNibName:#"PRoductSingleViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController: _productSingleViewController];
[self.window addSubview:navController.view];
[navController release];
[self.window makeKeyAndVisible];
return YES;
}
In my story board I have:
NavController-> View1(root) -> View2 ->View3 ->View4
I can normally use the flow in that order and navigation between segues is easy.
But How to go from View1 to View4 .. and from View4 there is back button should take you to View3?
Because if I made direct Segue From View1 to View4 ,, I cant reach View3.
Please let me know how to do this. Thanks in advance.
Here is an example with 3 viewcontrollers only but it will be the same logic:
You need to create segue between viewcontrollers like this:
viewController1 --- segueFrom1To2 ----- viewController2
viewController1 --- segueFrom1To3 ----- viewController3
viewController2 --- segueFrom2To3 ----- viewController3
Then you need a property in your viewController2, let's say: goToNextVc;
So, in your viewController1, you will be able to set this property when your perform the appropriate segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
ViewController2 *vc2 = segue.destinationViewController;
if ([segue.identifier isEqualToString:#"segueFrom1To3"])
{
vc2.goToNextVc = YES;
}
else{
vc2.goToNextVc = NO;
}
}
And now in the viewWillAppear of viewController2, simply add:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
if (self.goToNextVc){
self.goToNextVc = NO;
[self performSegueWithIdentifier:#"segueFrom2To3" sender:self];
}
}
If you don't want to see the transition between viewController1 and viewController2, simply create a custom segue, let's say SegueNoTransition in which you need to override perform:
-(void) perform{
[[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:NO];
}
And replace the type "show" of segueFrom1To3 with "custom" and the class with SegueNoTransition;
for (UIViewController *controller in self.navigationController.viewControllers) {
if ([controller isKindOfClass:[yourViewController class]]) {
[self.navigationController popToViewController:controller
animated:YES];
}
else {
NSLog(#"Not Found");
}
}
// You must import the viewController where you want to move.
// yourViewController = viewController where you want to go in example.
I'm using MMdrawerController to practice side drawer controllers in my project. But when I tap on the menu button on the left side, then tap to the row that selected, it opens just fine, but the corresponding view doesn't show menu item (drawer menu button) on the left of the view. So I can't make any further selection.
And I use storyboard.
Sample code from MydrawerController
I navigate through cells in drawer's tableview.
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate drawerMenuViewController:self didSelectMenuSection:indexPath.row];
switch ((MMDrawerMenuViewControllerSection)indexPath.row) {
case MMDrawerMenuViewControllerSectionProfile:
[self.mm_drawerController setCenterViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"MMProfileViewController"] withCloseAnimation:YES completion:nil];
break;
default:
break;
}
}
My center view is simply does these:
-(void)setupLeftMenuButton{
MMDrawerBarButtonItem *leftDrawerButton=[[MMDrawerBarButtonItem alloc]initWithTarget:self action:#selector(leftDrawerButtonPress:)];
[self.navigationItem setLeftBarButtonItem:leftDrawerButton animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)leftDrawerButtonPress:(id)sender{
[self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil];
}
-(void)doubleTap:(UITapGestureRecognizer*)gesture{
[self.mm_drawerController bouncePreviewForDrawerSide:MMDrawerSideLeft completion:nil];
}
And my other view controller that can't display menu item has the same button handlers given above. But still not showing the menu item.
It's not possible to push the controller into centerView from the side view, I have done this but need to use the delegate, I have define a protocol into left side controller like
#protocol LGSettingDrawerControllerDelegate <NSObject>
- (void)pushControllerWithIndex:(NSIndexPath*)indexPath;
#end
#property (nonatomic, weak) id<LGSettingDrawerControllerDelegate> delegate;
and in
in
.m file
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (_delegate != nil) {
[_delegate pushControllerWithIndex:indexPath];
}
[self.menuContainerViewController setMenuState:MFSideMenuStateClosed];
}
and this delegate method do push at center view controller like
[self.mm_drawerController setCenterViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"MMProfileViewController"] withCloseAnimation:YES completion:nil];
Up until now I have used prepareForSegue method to notify a view controller he is about to review a segue from a different view controller, but if i'm the vc that sending the segue, will my prepareForSegue (in he same class of the segue sender) will check if there is a code to preform before he fires the segue?
I'm asking because I got a solution here to preform a simple segue just to go from stable view row which represent notes to the creation page of the note for editing...very simple, but from some reason it's not presenting my note in the creation page, only takes me to a new clean creation page..
So I though maybe it's because the prepareForSegue method..here you can see the question to give you more details How to to create a segue to push back to editing mode from a table view?
Would really appreciate to get your help on this.
This are the 2 methods I added to my NotesList table view controller:
#pragma mark - delegate
- (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"noteSegue" sender:nil];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"noteSegue"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NMNote *note = [self.notes objectAtIndex:indexPath.row];
NMCreateNotesViewController *destination = [segue destinationViewController];
destination.textField.text = note.content;
}
}
And I made sure the segue identifier in the storyboard is named noteSegue. So why its note working...Im getting a new TextView instead of populating it with the note content :/
This is the error i'm getting:
Thanks!
The prepareForSegue: method is called on the current VC not the destination. UIViewController Class Reference. You can use the method to obtain a reference to the destination VC and set variables/call methods on it before the segue is performed.
Calling the segue like this:
[self performSegueWithIdentifier:#"NAMEOFSEGUE" sender:nil];
You can then prepare the destination VC like this:
#import "NMCreateNotesViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"NAMEOFSEGUE"]) {
NMCreateNotesViewController *destination = [segue destinationViewController];
[destination createNote]; /* or something similar */
}
}
EDIT:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NMNote *note = [self.notes objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"noteSegue" sender:note];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"NAMEOFSEGUE"]) {
NMCreateNotesViewController *destination = [segue destinationViewController];
NMnote *note = (NMNote*) sender;
destination.textField.text = note.content;
}
}
To check if there is a code to preform before the segue you can use solution below:
if ([segue.destinationViewController isKindOfClass:[UIViewController class]]) {
UIViewController *nextVC = segue.destinationViewController;
if(nextVC respondsToSelector:#selector(yourMethod)){
[nextVC yourMethod];
}
}
You can check if you are sure to perform the specific segue by implementing this method
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
If you have your note presenting code in viewDidLoad, try moving that to viewDidAppear (in your note editing VC). There used to be an issue when presenting from a TableView row where the segue would fire after VDL was executed.
I have 2 simple views in my Storyboard and both use the same ViewController. I have the following code to switch the the second view...
self.labelStatus.text = #"CHECKING";
[self performSegueWithIdentifier:#"LoginSuccess" sender:sender];
*labelStatus is on the second view.
For some reason the labels text is not changing. Can anyone advise why this is?
[self performSegueWithIdentifier:#"LoginSuccess" sender:sender];
//change the label value in prepareForSegue: method
- (void)prepareForSegue:(UIStoryboardSegue *)segue {
if ([segue.identifier isEqualToString:#"LoginSuccess"]) {
UIViewController *loginVC = segue.destinationViewController;
//declare the string in .h file and assign it in viewDidLoad in loginVC
loginVC.labelString = #"CHECKING";
}
}
in loginViewController.h file
#property(nonatomic, strong) NSString *labelString;
in loginViewController.m file
- (void)viewDidLoad
{
[super viewDidLoad];
self.labelStatus.text = labelString
}
You can customise your destinationViewController (properties, views, etc) in prepareForSegue: method, which is called before segue is executed:
- (void)prepareForSegue:(UISegue *)segue {
if ([segue.identifier isEqualToString:#"mySegue"]) {
UIViewController *destVC = segue.destinationViewController;
// do your customisation
}
}
Edit-
If you need to change the label in the same view controller, then you should modify the label in the prepareForSegue method.