I have one original view controller with four destination view controllers. I want to be able to push segue with a navigation controller to ALL of the destination view controllers from the original. I have tried...
- (IBAction)notificationsButtonPushed:(id)sender {
NotificationsViewController *notifications = [[NotificationsViewController alloc]init];
[self.navigationController pushViewController:notifications animated:YES];
}
- (IBAction)messagesButtonPushed:(id)sender {
MessagesViewController *messages = [[MessagesViewController alloc] init];
[self.navigationController pushViewController:messages animated:YES];
}
- (IBAction)settingsButtonPushed:(id)sender {
if (canMessage) {
SettingsViewController *settings = [[SettingsViewController alloc]init];
[self.navigationController pushViewController:settings animated:YES];
}
else {
NSLog(#"Can't Message");
}
}
- (IBAction)previewButtonPushed:(id)sender {
PreviewViewController *preview = [[PreviewViewController alloc]init];
[self.navigationController pushViewController:preview animated:YES];
}
and this just gives me an empty view controller without my UI components.
Note: I also have tired "initWithNidName:" and passed in the storyboardID of each destination view controller and it gives me Error:
'Could not load NIB in bundle: 'NSBundle </var/mobile/Applications/B7E025E5-D7D2-4FFD-B49C-E10DF5E94C44/LifePoints.app> (loaded)' with name 'preview'
I also have tried... (with storyboard segues set to "Push")
- (IBAction)notificationsButtonPushed:(id)sender {
[self performSegueWithIdentifier:#"notifications" sender:self];
}
- (IBAction)messagesButtonPushed:(id)sender {
if (canMessage) {
[self performSegueWithIdentifier:#"messages" sender:self];
}
else {
NSLog(#"Can't Message");
}
}
- (IBAction)settingsButtonPushed:(id)sender {
[self performSegueWithIdentifier:#"settings" sender:self];
}
- (IBAction)previewButtonPushed:(id)sender {
[self performSegueWithIdentifier:#"preview" sender:self];
}
Although this does push the destination view controllers onto the screen with the appropriate segue type, it does not segue to the correct destination view controller. It only seems to segue to the last attached storyboard segue.
Does anyone know how to correctly apply this and have it behave in the form I am looking for?
EDIT
It is important to note I am checking to see if a condition is met the "messagesButtonPushed" method. I am checking to see if user is allowed to message, and if they are, then segue to the VC.
You don't need any code to implement the basic segues.
In your story board ensure that your original view controller is embedded in a navigation controller (Select the original View and select Edit->Embed in->Navigation Controller).
Then you can simply control drag from each of your four buttons to the corresponding destination views, selecting "push" as the segue type. You can then click on the segue icon between the two views to give each segue an identifier.
You can delete your IBAction methods and any actions on the buttons that link to them.
I suggest you complete a Storyboard Tutorial to learn how storyboards and segues work.
If you do want to perform a segue programatically then you can control drag from the yellow view controller icon at the bottom of the view to the destination. You can then invoke the segue with performSegueWithIdentifier as per your second code - In your case your could have two different segues and trigger the store segue or the other one depending on purchase status
Related
I am trying to transition between view controllers with a segue. It is navigating correctly but it is running twice.
Here is my code:
- (IBAction)onActionNext:(id)sender {
if ([[nameField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]isEqualToString:#""]) {
}else if([[emailField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:#""]){
}else if([[mobileField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:#""]){
}else{
[self performSegueWithIdentifier:#"VerifyNext" sender:sender];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"VerifyNext"]) {
// Get destination view
OTPSceneViewController *vc = [segue destinationViewController];
[vc setTitle:#"myapp"];
}
}
If even after commenting this line segue still run then as #mikealter said segue is connected your button. Check if when you check for when you click segue what it highlights.
A button like:
Or A View Controller like:
If you fall in first case that means your segue is connected to a button and, that is the reason why it opens next page even if you comment your code. And 2 times when you keep code(one from storyboard and another from code).
Edit:
If you want to call next page from code only. Remove Segue. Then reconnect it with view controller like:
First, whenever you create a segue (i.example) FROM a Button TO a View Controller, you are creating the link between VC1 and VC2.
If you run your project you will see that the segue works navigating from one VC to the other one.
This means that it already performs the segue, so if you declare an ACTION for your button and make a call to - perform segue... - it will run twice.
Note: if you call performSegueWithIdentifier is probably because you want to pass some data via SENDER. In that case you (obviously) need to call the segue ONCE, so instead of creating the segue from the button to the next VC, (first delete that segue), create a new one FROM the VCOrigin to VCDestination -> there are many ways to do it, but from the Interface Builder is as simple as right click on the top yellow rounded icon and drag&drop on the destinationVC. Don't forget to set the identifier on the Attributes Inspector.
Now, in your button~Action make the call to performSegueWithIdentifier and pass the data.
if you stop storyboard segue use following method:
override func shouldPerformSegueWithIdentifier(identifier: String,sender: AnyObject?) -> Bool {
return false
}
You don't even add
[self performSegueWithIdentifier:#"VerifyNext" sender:sender];
in onActionNext method if you add the segue in storyboard
If you want to navigate to next view controller use above line.
Preparing for the Segue
In order to pass information from one view controller to another using
the UIStoryboardSegue object we’ll need to take advantage of the
current view controller’s prepareForSegue:sender: method.
But If you call both,it calls two times.So make sure that whether you want to pass information or data to next view controller or just navigate the view.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"prepareForSegue: %#", segue.identifier);
if([segue.identifier isEqualToString:#"VerifyNext"])
{
VerifyNextViewController *verifyNVC = segue.destinationViewController;
verifyNVC.name = #"Prashabt";
verifyNVC.id = #"1234";
}
else if([segue.identifier isEqualToString:#"VerSeg"])
{
AnotherViewController *anotherVC = segue.destinationViewController;
anotherVC.name = #"Sharma";
anotherVC.id = #"3245";
}
}
have a look of this question this may sure rectify
or there is another way to navigate to another view controller using below code.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"viewControllerName"];
[self.navigationController pushViewController:vc animated:YES];
make sure you give identity to the view controller using identity inspector see image.
I have a uibarbuttonitem called nextButton in storyboard that goes to another ViewController with a segue. But when I try to add this method on another file:
- (IBAction)nextButtonPressed:(id)sender {
NSLog(#"Hello")
}
the output does not show up on the console. The only way I have gotten the button to display this message is when I take out the segue in the storyboard. When I do that, I add the following method so that on clicking the button the segue is done programmatically:
- (IBAction)nextButtonPressed:(id)sender {
NSLog(#"Hello");
GameDetailsTableViewController *gameDetails = [[GameDetailsTableViewController alloc]init];
[self.navigationController pushViewController: gameDetails animated:YES];
}
But when I do this, the console shows an error:
'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
I need help to figure out how to make sure that the button displays the message and goes to the next view controller on click.
A button can be assigned to either an IBAction method or a segue.
If you use a segue, implement -prepareForSegue:sender: in your controller to run any additional code when the segue is executed. E.g.:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([[segue identifier] isEqual:#"gameDetailSegue"])
{
GameDetailsTableViewController* gameDetails = (id)[segue destinationViewController];
gameDetails.cheatMode = self.prefs.cheatModeEnabled;
}
}
If you use an action method then you have to present the view controller manually. You also have to instantiate the view controller from your storyboard:
- (IBAction)nextButtonPressed:(id)sender
{
GameDetailsTableViewController* gameDetails =
[self.storyboard instantiateViewControllerWithIdentifier:#"gameDetailController"]
gameDetails.cheatMode = self.prefs.cheatModeEnabled;
[self.navigationController pushViewController:gameDetails animated:YES];
}
In either case, you must assign identifiers to your segues and view controllers in your storyboard.
I have one button (back button) on a view controller. Simple so far.
I have 2 view controllers with a table on each one.
if a user selects a row from either table it goes to the view controller with the back button on.
The back button needs to go back to the original view controller that the user was on when they selected the row.
I was considering unwind segues for this which is fine, but I cannot add two segues to one button. One for return to one table view and a return for the other table view dependent on which table view they used to access the view controller.
Any ideas ?
As Volk explained,
-(IBAction)devDismiss
{
NSLog(#" ------- dev dismiss .....");
// for a custom segue unwind, you must do this...
[self performSegueWithIdentifier:#"specialWord" sender:self];
// "specialWord" is the identifier set on storyboard for the
// custom unwind segue
/* for a "default" unwind segue, do this...
[self dismissViewControllerAnimated:YES completion:nil];
Note, if you used a push segue, you should use
[self.navigationController popViewControllerAnimated:YES]
If you used a modal segue, you should use
[self dismissViewControllerAnimated:YES completion:nil] "
*/
}
Note that indeed, you must also use "specialWord" in your segueForUnwindingToViewController: override, which will be in the DESTINATION (that is to say the ORIGINAL) view controller, the one underneath, that is to say.
-(UIStoryboardSegue *)segueForUnwindingToViewController:
(UIViewController *)toViewController
fromViewController:(UIViewController *)fromViewController
identifier:(NSString *)identifier
{
NSLog(#"we're in _your class name_, segueForUnwindingToViewController %#",
identifier);
// for some unwinds, we have a custom unwind we want to use.
// so, check the identifier:
if ([identifier isEqualToString:#"specialWord"])
{
YourCustomUnwindSegue *sg = [[YourCustomUnwindSegue alloc]
initWithIdentifier:identifier
source:fromViewController
destination:toViewController];
return sg;
}
// don't forget the break-away "return" inside any macthes.
// NSLog(#"note, if this message appears, it's likely you have a typo
// somewhere for 'specialWord' - unless you genuinely have a situation
// where it will also fall through to the 'default' unwind segue :O ");
// BE SURE TO return the default unwind segue otherwise
return [super segueForUnwindingToViewController:toViewController
fromViewController:fromViewController
identifier:identifier];
}
Okay, so in the process of developing my newest app, I found that my storyboard got huge, so in an effort to clean it up some, i have divided it into multiple storyboards before it gets out of hand. just for settings alone i have roughly 20 tableviewcontrollers that branch out from a root NavigationController. That navigationcontroller was a TabItem on a TabBarController, which is the application's root view controller.
I've moved the TabBar into it's own StoryBoard as the Root_Storyboard and the Navigation controller is now the initial view of the Settings_Storyboard.
Just for testing purposes, I placed a few UIViewControllers as tab items in the TabBarController (Root_Storyboard) and subclassed one and added the following code to it's viewWillAppear method. It works great, but I know that the presentViewController displays the NavigationController modally and hides the tabBar. Obviously I don't want that, how do I get it to push properly so that the TabBar remains visible?
- (void) viewWillAppear:(BOOL)animated {
UIStoryboard *settingsStoryboard = [UIStoryboard storyboardWithName:#"Settings_iPhone" bundle:nil];
UIViewController *rootSettingsView = [settingsStoryboard instantiateInitialViewController];
[self.tabBarController presentViewController:rootSettingsView animated:NO completion:NULL];
}
Edit - To clarify. The above code is the subclassed method for a UIViewController (child of UITabBarController:index(1)) in the Root_iPhone.storyboard. The UINavigationController/UITableViewController that I am trying to load is found in Settings_iPhone.storyboard. Not sure how to implement the linkView suggested below in this situation.
This is quite possible and a smart move - decluttering your Storyboards presents cleaner interface files to dig through, reduced loading times in XCode, and better group editing.
I've been combing across Stack Overflow for a while and noticed everyone is resorting to Custom Segues or instantiating tab based setups programmatically. Yikes. I've hacked together a simple UIViewController subclass that you can use as a placeholder for your storyboards.
Code:
Header file:
#import <UIKit/UIKit.h>
#interface TVStoryboardViewController : UIViewController
#end
Implementation file:
#import "TVStoryboardViewController.h"
#interface TVStoryboardViewController()
#property (nonatomic, strong) UIViewController *storyboardViewController;
#end
#implementation TVStoryboardViewController
- (Class)class { return [self.storyboardViewController class]; }
- (UIViewController *)storyboardViewController
{
if(_storyboardViewController == nil)
{
UIStoryboard *storyboard = nil;
NSString *identifier = self.restorationIdentifier;
if(identifier)
{
#try {
storyboard = [UIStoryboard storyboardWithName:identifier bundle:nil];
}
#catch (NSException *exception) {
NSLog(#"Exception (%#): Unable to load the Storyboard titled '%#'.", exception, identifier);
}
}
_storyboardViewController = [storyboard instantiateInitialViewController];
}
return _storyboardViewController;
}
- (UINavigationItem *)navigationItem
{
return self.storyboardViewController.navigationItem ?: [super navigationItem];
}
- (void)loadView
{
[super loadView];
if(self.storyboardViewController && self.navigationController)
{
NSInteger index = [self.navigationController.viewControllers indexOfObject:self];
if(index != NSNotFound)
{
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[viewControllers replaceObjectAtIndex:index withObject:self.storyboardViewController];
[self.navigationController setViewControllers:viewControllers animated:NO];
}
}
}
- (UIView *)view { return self.storyboardViewController.view; }
#end
Description:
The view controller uses its Restoration Identifier to instantiate a storyboard in your project.
Once loaded, it will attempt to replace itself in its
UINavigationController's viewController array with the Storyboard's
initial view controller.
When requested, this subclass will return the UINavigationItem of the Storyboard's initial view controller. This is to ensure that navigation items loaded into UINavigationBars will correspond to the view controllers after the swap.
Usage:
To use it, assign it as the subclass of a UIViewController in your Storyboard that belongs to a UINavigationController.
Assign it a Restoration ID, and you're good to go.
Setup:
And here's how you set it up in the Storyboard:
This setup shows a tab bar controller with navigation controllers as its first tab controllers. Each navigation controller has a simple UIViewController as its root view controller (I've added UIImageViews to the placeholders to make it easy to remember what it links to). Each of them is a subclass of TVStoryboardViewController. Each has a Restoration ID set to the storyboard they should link to.
Some wins here:
It seems to work best for modal presentations where the subclass is the root view controller of a navigation controller.
The subclass doesn't push any controllers on the stack - it swaps. This means you don't have to manually hide a back button or override tab behaviour elsewhere.
If you double tap on a tab, it will take you to the Storyboard's initial view, as expected (you won't see that placeholder again).
Super simple to set up - no custom segues or setting multiple subclasses.
You can add UIImageViews and whatever you like to the placeholder view controllers to make your Storyboards clearer - they will never be shown.
Some limitations:
This subclass needs to belong to a UINavigationController somewhere in the chain.
This subclass will only instantiate the initial view controller in the Storyboard. If you want to instantiate a view controller further down the chain, you can always split your Storyboards further and reapply this subclass trick.
This approach doesn't work well when pushing view controllers.
This approach doesn't work well when used as an embedded view controller.
Message passing via segues likely won't work. This approach suits setups where sections of interface are unique, unrelated sections (presented modally or via tab bar).
This approach was hacked up to solve this UITabBarController problem, so use it as a partial solution to a bigger issue. I hope Apple improves on 'multiple storyboard' support. For the UITabBarController setup however, it should work a treat.
This is a bit late for Hawke_Pilot but it might help others.
From iOS 9.0 onwards you can create a Relationship Segue to another storyboard. This means that Tab Bar View Controllers can link to View Controllers on another storyboard without some of the mind-bending tricks seen in other answers here. :-)
However, this alone doesn't help because the recipient in the other storyboard doesn't know it's being linked to a Tab Bar View Controller and won't display the Tab Bar for editing. All you need to do once you point the Storyboard Reference to the required View Controller is select the Storyboard Reference and choose Editor->Embed In->Navigation Controller. This means that the Nav Controller knows it's linked to a Tab Bar View Controller because it's on the same storyboard and will display the Tab Bar at the bottom and allow editing of the button image and title. No code required.
Admittedly, this may not suit everyone but may work for the OP.
Not sure if your question is answered, and for others looking for a solution to this problem, try this method.
Create the Tab Bar Controller with Navigation Controllers in one storyboard file. And add an empty view controller (I named it RedirectViewController) as shown in the picture.
The child view controller (let's call it SettingsViewController for your case) is located in Settings_iPhone.storyboard.
In RedirectViewController.m, code this:
- (void)viewWillAppear:(BOOL)animated
{
UIStoryboard *settingsStoryboard = [UIStoryboard storyboardWithName:#"Settings_iPhone" bundle:nil];
UIViewController *rootSettingsView = [settingsStoryboard instantiateInitialViewController];
[self.navigationController pushViewController:rootSettingsView animated:NO completion:nil];
}
SettingsViewController will be pushed into view instantly when Settings tab is touched.
The solution is not complete yet! You will see "< Back" as the left navigationItem on SettingsViewController. Use the following line in its viewDidLoad method:
self.navigationItem.hidesBackButton = YES;
Also, to prevent the same tab bar item from being tap and causes a jump back to the blank rootViewController, the destination view controllers will need to implement UITabBarControllerDelegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
return viewController != tabBarController.selectedViewController;
}
It works for me.
Add Following code to your LinkViewController
-(void) awakeFromNib{
[super awakeFromNib];
///…your custom code here ..
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:self.storyBoardName bundle:nil];
UIViewController * scene = nil;
// Creates the linked scene.
if ([self.sceneIdentifier length] == 0)
scene = [storyboard instantiateInitialViewController];
else
scene = [storyboard instantiateViewControllerWithIdentifier:self.sceneIdentifier];
if (self.tabBarController)
scene.tabBarItem = self.tabBarItem;
}
Here is the screenShot for LinkViewController .
LinkViewController is just a placeholder where new viewController would be placed. Here is the sample code which I used for my app.
RBStoryboardLink . Its working great for me. Let me know if it is helpful for you.
I am trying to make a mechanism to drill down a file / folder list. The idea is to show the same file list view controller every time the user selects a folder, and show a file detail view controller if he/she selects a file.
So far, I have created a segue from the file list view controller to the file detail view controller, and a segue from the file list table view cell to the the file list table view controller:
The issue with this is that as soon as the user taps the cell, the segue is executed. I would like to remove the segue from the table view cell and make one from the file list view controller to itself. That way, I could trigger the right segue programmatically when the user tapped the cell.
So, my question is: Is it possible to create a segue from a view controller to itself in Interface Builder?
If you are using a navigation controller you need to push the ViewController into the nav stack. In this example, i named my ViewController "VDI" in my Storyboard ID setting.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
YourVC *dest = [storyboard instantiateViewControllerWithIdentifier:#"VDI"];
[self.navigationController pushViewController:dest animated:YES];
If you don't want the NavigationController to keep adding itself into your "Back" history you can pop the stack before adding to it like so.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
YourVC *dest = [storyboard instantiateViewControllerWithIdentifier:#"VDI"];
UINavigationController *navController = self.navigationController;
[navController popViewControllerAnimated:NO];
[navController pushViewController:dest animated:YES];
Using Xcode 5 there is a much simpler solution.
Click the table cell in the storyboard
Open the Connections Inspector (right arrow icon in the upper right)
Under "triggered segues" you see "selection"
Drag from the circle next to "selection" to the cell in the storyboard
That's it.
I developed a method to create a segue using a phantom button. I believe it will solve your problem. You can read about it in my answer here.
Instead of performing a segue to the same controller, you can instantiate a view controller (the same one) from storyboard, and then push that onto the navigation controller.
Interface Builder approach: Just segue to a storyboard reference which refers back to the presenting view controller.
The correct answer is to use a Storyboard Reference that is referencing the UIViewController you want to segue to itself and then point the segue at it.
In IOS 6, there is a cleaner solution than using a phantom button. You can still define the segue from the table cell to the view controller, and look at the sender to cancel the automatically triggered segue:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//storyboards should use segues and override prepareForSegue instead
//but here we need custom logic to determine which segue to use
id item = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (item meets condition) {
[self performSegueWithIdentifier:#"segue1" sender:self];
} else {
[self performSegueWithIdentifier:#"segue2" sender:self];
}
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
//ignore segue from cell since we we are calling manually in didSelectRowAtIndexPath
return (sender == self);
}
Here's how you can push another instance of the current view controller without defining a segue or hardcoding its own identifier:
SameViewController *same = [self.storyboard instantiateViewControllerWithIdentifier: self.restorationIdentifier];
[self.navigationController pushViewController: same animated: YES];
You just need to set the Restoration ID to be the same as Storyboard ID (there's a checkbox for that in IB).
Hope this helps.
I found that you can create multiple prototype cells.
Than you can link every cell (in the Storyboard) to a different View.
Something like this:
NSString *CellIdentifier = #"Cell";
if (Condition2 ){
CellIdentifier = #"Cell2"; }
if (Condition3 ){
CellIdentifier = #"Cell3"; }