I have a ViewControllerA that has roughly 15 buttons in my case they represent levels, When a level is pressed it segues to ViewControllerB. Now it doesn't make sense to manually ctrl+drag and create segue for each button. I'm wondering how can a button segue to ViewControllerB and have a unique identifier then ill be passing an object in other words data to ViewControllerB to recognize which level the user pressed. I also want to point out that I have a push view controller.
So far one of my unsuccessful attempts were to do is to ctrl+drag all the buttons and create a method called:(the button title in my case is the level number)
- (IBAction)buttonPressed:(UIButton *)sender {
[self performSegueWithIdentifier:[sender currentTitle] sender:self];
}
afterwards I call the prepareForSegue:segue sender:sender method.To be honest Im new to iOS developing. Any helpful tutorial or article would be great for me.
Thanks in advance for all helpers.
You were close with your unsuccessful attempt. You only want one identifier for a single segue that you set up in IB directly from the controller to VCB. In your action method, that all the button's invoke, do this (notice that I'm passing the button in the sender argument):
- (IBAction)buttonPressed:(UIButton *)sender {
[self performSegueWithIdentifier:#"MySegueIdentifier" sender:sender];
}
Then in prepareForSegue, you can use the sender argument to get the button's title:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UIButton *)sender {
NSString *title = sender.currentTitle;
// do what you need with the title
}
Create a private property on ViewControllerA (perhaps using an enum) to make note of which level is selected.
In your - (IBAction)buttonPressed:(UIButton *)sender method, you need to figure out which button was pressed, and use that information to set the property. Then call [self performSegueWithIdentifier:#"nameOfStoryboardSegue" sender:self];
Create a public property on ViewControllerB to keep track of which level is selected.
Override the prepareForSegue:segue sender:sender method. Instantiate the destination view controller as a ViewControllerB, and set the property on B to match the selected property from A.
Perform the segue.
This question & answer will help you with passing the data between views in the prepareForSegue method, and this question & answers might help with handling all the buttons in the single IBAction method.
I know this is an old question, but I found a simple solution...
duplicate the View Controller from your storyboard and assign the same class to it in identity inspector. You will have a duplicate "storyboard view controller" connected to the same code. With its own icon, tag and storyboard ID
Related
I just need to know how to make some 'conditional' redirect / segue to some view. For example :
If user haven't login yet, then a view with login form appears. But if user logged in already, they will see their profile view.
How to make conditional segue like that using storyboard?
Thank you
For conditional segues, don't start a segue from a button as you normally do.
Instead of that use the following steps:
In your storyboad, start a segue directly from the source view controller to the destination view controller. For doing this, you can drag your source view controller's icon on the bar just below the view to the destination view controller on the main canvas area. Just remember you have to connect two view controllers directly and not with any control.
Enter a suitable segue identifier for this segue. For example say "conditionSegue"
Now, in your source view controller’s .m file perform your condition and call -performSegueWithIdentifier:sender: method on "self" like this:
-(void)loadDestinationVC{
if(condition == YES){
[self performSegueWithIdentifier:#"conditionSegue" sender:nil];
}
}
I hope I made it clear.
I believe a better approach if you want to use segues from buttons would be to use this:
First create a button and use a segue to connect it to another view, give this segue a name
then use this method in your .m file
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"SegueName"]) {
//Check if should make the segue
//if not do something
[self doSomething];
//and return NO;
return NO;
}
return YES;
}
In Swift you could do :
as #Gaurav Singh suggested in his answer
Step 1: Insert a segue from ViewControllerA to ViewControllerB. The segue should start from ViewControllerA itself and not from any control ( like Button ) in it.
Step 2: Give segue an unique identifier in storyboard. Ex: seageFromAtoB
Step 3 : In your ViewControllerforA.swift write :
if(condition == true) {
self.performSegueWithIdentifier("seageFromAtoB", sender: self)
}
If you are performing some task in some other Thread then using performSegueWithIdentifier can throw an exception.
If you get this error then you should use :
if(condition==true){
NSOperationQueue.mainQueue().addOperationWithBlock {
self.performSegueWithIdentifier("seageFromAtoB", sender: self)
}
}
This will perform segue redirection in main Thread.
Hope this help for someone looking for Swift option
You make the segues directly from one controller to another (or others), and give them identifiers. In code you call choose between segues depending on your conditions, and call performSegueWithIdentifier:sender:.
Create "generic" segue by ctrl dragging from the view controller icon at the bottom to the destination. This segue won't be associated with any action. Then, in your code where ever the event is using your conditional code that you want to trigger the segue, call:
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender
I connected first view controller with the second one using StoryBoard Push Segue and Interface Builder.
The button is named GO on top/right.
I have three textfield that must be filled before going to second controller.
I display an alert when one of them is empty.
The problem is that my code after displaying correct alertView goes to SecondController instead of remaining on mainController.
if ([segue.identifier isEqualToString:#"DataDisplay"])
{
if (![self verifySelection]) {
return;
} else {
RowViewController *rowViewController = segue.destinationViewController;
// rowViewController.delegate = self;
}
}
1) You have a segue wired directly from your Go button to your Sensor Data view controller. You don't want this, because anytime someone touches Go, the segue is going to happen ... no stopping it. So, first step is to remove the segue you have going from Go to your second view controller.
2) Instead, wire the segue from the File's Owner icon below the view controller to the second view controller. Give it a name like DataDisplay.
3) In the IBAction for your Go button
if ([self verifySelection) {
[self performSegueWithIdentifier:#"DataDisplay" sender:self]
}
An easy fix would be to create the segue manually, rather than letting the interface builder manage it. So you would ctrl-drag from your main view controller to your second one, selecting push as the type of segue and assigning it an identifier through the identifier inspector, then you connect an IBAction to your Go button and in the method you perform the checks on the text fields before programmatically firing the segue with:
[self performSegueWithIdentifier:#"whateverIdentifierYouGaveYourSegue" sender:self];
Heads up: to create a manual segue from a viewcontroller to another one, you need to either zoom out in your storyboard or ctrl-drag from the yellow circle underneath the view!
Edit: Your IBAction connected to the button method should be something like the following:
- (IBAction)download:(id)sender {
if(text boxes are ok)
[self performSegueWithIdentifier:#"segueIdentifier" sender:self];
else
[self showWarning];
}
Make sure that you assigned the ID segueIdentifier to the segue you created in your storyboard.
Your problem is you are defining the "performSegueWithIdentifier" after displaying the alert.
I think the code you are doing is like this :
//AlertView Allocation
[alert show];
Perform Segue
If this is how you are doing, then you are doing it wrong.
You have to use the structure of If-Else Statements and put up the Perform Segue in the condition where all the textfields are filled.
I'm using storyboard, and in my storyboard I got a button which I connected with another view controller. So when you click the button, the other view controller shows up. I connected this using an Action Segue - Push in the storyboard. I also connected the button with an IBAction property. The problem is when I click the button it first goes to the view controller that its connected to, and after that it executes the IBAction function. How can I change this order?
You cannot change that order. There are two things that you can do.
First:
Use the segue only.
Overwrite prepareForSegue: and place your code there. If there is more than one segure from that view controller then you can distinguish within prepareForSegue: which one is currently being performed. For that you should provide them with unique names/segue IDs.
Second:
Use the IBAction only.
Within the IBAction method, at its very end, call performSegueWithIdentifier:sender:. Again, for that you will have to name all your segues with unique segue IDs.
The question to ask yourself is, why are you using both? Pick one and stick with it IMO.
If you want to perform some actions when the segue happens you should implement
prepareForSegue:
Update to address comment
I actually don't know how I can do it in code, how can I load a ViewController that is on my storyboard without using the Action Segue Push?
The easiest way that I've found is in your Storyboard you can set an identifier for the view controller.
Notice Storyboard ID is set to MyViewController
Now in your IBAction method you can do the following.
UIViewController *myViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"MyViewController"];
[self presentViewController:myViewController animated:YES completion:nil];
I solved this by chaining the IBAction from my segue. I find this approach the most useful as I can have a "save" action without a segue (chain a button to the IBAction, sometimes useful), and also use a segue for dismissing the view controller when necessary.
-(IBAction)save:(id)sender
{
/* Do some Save stuff */
}
Then in my prepareForSegue:sender:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"save"])
{
[self save:sender];
}
}
Link up your non-segue buttons to the IBAction, and segue your unwind or other such buttons with an identifier as "save". Magic.
I have a common UIViewController that all my UIViewsControllers extend to reuse some common operations.
I want to set up a segue on this "Common" UIViewController so that all the other UIViewControllers inherit.
I am trying to figure out how do I do that programmatically.
I guess that the question could also be how do I set a segue for all my UIViewControllers without going into the story board and do them by hand.
I thought I would add another possibility. One of the things you can do is you can connect two scenes in a storyboard using a segue that is not attached to an action, and then programmatically trigger the segue inside your view controller. The way you do this, is that you have to drag from the file's owner icon at the bottom of the storyboard scene that is the segueing scene, and right drag to the destination scene. I'll throw in an image to help explain.
A popup will show for "Manual Segue". I picked Push as the type. Tap on the little square and make sure you're in the attributes inspector. Give it an identifier which you will use to refer to it in code.
Ok, next I'm going to segue using a programmatic bar button item. In viewDidLoad or somewhere else I'll create a button item on the navigation bar with this code:
UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:#"Buttonize"
style:UIBarButtonItemStyleDone
target:self
action:#selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = #[buttonizeButton];
Ok, notice that the selector is buttonizeButtonTap:. So write a void method for that button and within that method you will call the segue like this:
-(void)buttonizeButtonTap:(id)sender{
[self performSegueWithIdentifier:#"Associate" sender:sender];
}
The sender parameter is required to identify the button when prepareForSegue is called. prepareForSegue is the framework method where you will instantiate your scene and pass it whatever values it will need to do its work. Here's what my method looks like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"Associate"])
{
TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
translationQuizAssociateVC.contentID = self.contentID;
translationQuizAssociateVC.index = self.index;
translationQuizAssociateVC.content = self.content;
}
}
I tested it and it works.
By definition a segue can't really exist independently of a storyboard. It's even there in the name of the class: UIStoryboardSegue. You don't create segues programmatically - it is the storyboard runtime that creates them for you. You can normally call performSegueWithIdentifier: in your view controller's code, but this relies on having a segue already set up in the storyboard to reference.
What I think you are asking though is how you can create a method in your common view controller (base class) that will transition to a new view controller, and will be inherited by all derived classes. You could do this by creating a method like this one to your base class view controller:
- (IBAction)pushMyNewViewController
{
MyNewViewController *myNewVC = [[MyNewViewController alloc] init];
// do any setup you need for myNewVC
[self presentModalViewController:myNewVC animated:YES];
}
and then in your derived class, call that method when the appropriate button is clicked or table row is selected or whatever.
I've been using this code to instantiate my custom segue subclass and run it programmatically. It seems to work. Anything wrong with this? I'm puzzled, reading all the other answers saying it cannot be done.
UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:#"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];
Guess this is answered and accepted, but I just would like to add a few more details to it.
What I did to solve a problem where I would present a login-view as first screen and then wanted to segue to the application if login were correct. I created the segue from the login-view controller to the root view controller and gave it an identifier like "myidentifier".
Then after checking all login code if the login were correct I'd call
[self performSegueWithIdentifier: #"myidentifier" sender: self];
My biggest misunderstanding were that I tried to put the segue on a button and kind of interrupt the segue once it were found.
You have to link your code to the UIStoryboard that you're using. Make sure you go into YourViewController in your UIStoryboard, click on the border around it, and then set its identifier field to a NSString that you call in your code.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
YourViewController *yourViewController =
(YourViewController *)
[storyboard instantiateViewControllerWithIdentifier:#"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];
For controllers that are in the storyboard.
jhilgert00 is this what you were looking for?
-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:#"HomeController"];
[self.navigationController pushViewController: myController animated:YES];
}
OR...
[self performSegueWithIdentifier:#"loginMainSegue" sender:self];
well , you can create and also can subclass the UIStoryBoardSegue . subclassing is mostly used for giving custom transition animation.
you can see video of wwdc 2011 introducing StoryBoard. its available in youtube also.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIStoryboardSegue_Class/Reference/Reference.html#//apple_ref/occ/cl/UIStoryboardSegue
I'd like to add a clarification...
A common misunderstanding, in fact one that I had for some time, is that a storyboard segue is triggered by the prepareForSegue:sender: method. It is not. A storyboard segue will perform, regardless of whether you have implemented a prepareForSegue:sender: method for that (departing from) view controller.
I learnt this from Paul Hegarty's excellent iTunesU lectures. My apologies but unfortunately cannot remember which lecture.
If you connect a segue between two view controllers in a storyboard, but do not implement a prepareForSegue:sender: method, the segue will still segue to the target view controller. It will however segue to that view controller unprepared.
Hope this helps.
Storyboard Segues are not to be created outside of the storyboard. You will need to wire it up, despite the drawbacks.
UIStoryboardSegue Reference clearly states:
You do not create segue objects directly. Instead, the storyboard
runtime creates them when it must perform a segue between two view
controllers. You can still initiate a segue programmatically using the
performSegueWithIdentifier:sender: method of UIViewController if you
want. You might do so to initiate a segue from a source that was added
programmatically and therefore not available in Interface Builder.
You can still programmatically tell the storyboard to present a view controller using a segue using presentModalViewController: or pushViewController:animated: calls, but you'll need a storyboard instance.
You can call UIStoryboards class method to get a named storyboard with bundle nil for the main bundle.
storyboardWithName:bundle:
First of, suppose you have two different views in storyboard, and you want to navigate from one screen to another, so follow this steps:
1). Define all your views with class file and also storyboard id in identity inspector.
2). Make sure you add a navigation controller to the first view. Select it in the Storyboard and then Editor >Embed In > Navigation Controller
3). In your first class, import the "secondClass.h"
#import "ViewController.h
#import "secondController.h"
4). Add this command in the IBAction that has to perform the segue
secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:#"second"];
[self.navigationController pushViewController:next animated:YES];
5). #"second" is secondview controller class, storyboard id.
I reverse-engineered and made an open source (re)implementation of UIStoryboard's segues: https://github.com/acoomans/Segway
With that library, you can define segues programmatically (without any storyboard).
Hope it may help.
A couple of problems, actually:
First, in that project you uploaded for us, the segue does not bear the "segue1" identifier:
no identifier
You should fill in that identifier if you haven't already.
Second, as you're pushing from table view to table view, you're calling initWithNibName to create a view controller. You really want to use instantiateViewControllerWithIdentifier.
Here is the code sample for Creating a segue programmatically:
class ViewController: UIViewController {
...
// 1. Define the Segue
private var commonSegue: UIStoryboardSegue!
...
override func viewDidLoad() {
...
// 2. Initialize the Segue
self.commonSegue = UIStoryboardSegue(identifier: "CommonSegue", source: ..., destination: ...) {
self.commonSegue.source.showDetailViewController(self.commonSegue.destination, sender: self)
}
...
}
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// 4. Prepare to perform the Segue
if self.commonSegue == segue {
...
}
...
}
...
func actionFunction() {
// 3. Perform the Segue
self.prepare(for: self.commonSegue, sender: self)
self.commonSegue.perform()
}
...
}
My problem seems like a generic problem, yet can't seem to find an answer for it.
I have a situation where when the user taps on a custom UITableViewCell, I would like to display an alert and then based on the response to the alert, either stay on the same view (user selecting cancel) or display another view (if the user selects proceed). And I would like to do this using the storyboard feature & segues.
How would one go about this? Do you have to do this the old fashioned way?
#user, Just create the alertView the old fashion way; I do know of any storyboard feature to do this differently. Where storyboard can help is with the segues. You can call the segues programmatically. With you alert view cancel button you can just return (i.e. do nothing). For the other option, to display another view, you can programmatically call a segue to transition to the desired view. If you don't have the proper segue already defined for some other reason on your storyboard, just create a button out and use that to create the segue and name it. Name the segue by clicking on it in storyboard and use the attributes inspector to give it name (identifier). Then hide the button or put it out of the view. I typically put these type of button on the toolbar and use spacers to keep them out of the view. Here's some sample code:
Call the segue from the alert view delegate like this:
[self performSegueWithIdentifier: #"done" sender: self];
Also implement this method to do any necessary task to prepare for the segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"done"])
{
// [[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
// [[segue destinationViewController] setSelectedClient:selectedClient];
}
}
You can create segues directly from the startingViewController to multiple destinationViewControllers that can then be "performed" programmatically. You do not need to create any hidden buttons for them, which does seem like a hack.
OK I came up with a solution in keeping with the storyboard that I like.
Example:
My tableview has 2 sections, grouped, and cells are dynamic prototype. Section 0 contains one row/UITableViewCell & I don't want it to segue. Section 1 contains multiple cells that I want to trigger the segue & drill down into the detail.
In Storyboard:
I removed the segue linking the tableviewcell to the destination view controller.
I made a 'generic' segue linking the source view controller directly to the destination view controller.
In the attributes on the segue, I set the identifier ('EditTimePeriod') and set the type to Push (I presume Modal would work just the same).
In the source view controller:
In the prepareForSegue method I handled both the common 'AddTimePeriod' segue I control-dragged from my UIBarButtonItem (Add), along with the 'generic'(vc-->vc) 'EditTimePeriod' segue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// handle the click of the 'Add' bar button item
if([segue.identifier isEqualToString:#"AddTimePeriod"]) {
TimePeriodViewController* tpvc = (TimePeriodViewController*)segue.destinationViewController;
tpvc.delegate = self;
// database & entity stuff for adding the new one to the mOC, etc
}
// handle the click of one of the 'editable' cells -
if([segue.identifier isEqualToString:#"EditTimePeriod"]) {
TimePeriodViewController* tpvc = (TimePeriodViewController*)segue.destinationViewController;
tpvc.delegate = self;
TimePeriod * newTP = [self.timePeriodArray objectAtIndex:self.tableView.indexPathForSelectedRow.row];
tpvc.timePeriod = newTP;
}
}
Then I implemented the tableView:didSelectRowAtIndexPath method, and put my condition in here. If the selected row was outside of section zero I called the EditTimePeriod segue manually, defining the sender as the selected tableviewcell:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(self.tableView.indexPathForSelectedRow.section!=0){
[self performSegueWithIdentifier:#"EditTimePeriod" sender:[tableView cellForRowAtIndexPath:indexPath]];
}
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
return;
}
would be nice to code the cell in section 0 so that it is not selectable in the first place!
Hope this helps though.
** and then 5 minutes later I took another look and realized I could just move the data from section 0 into the section header, which is more intuitive and wasn't being used anyway. leaving the design open for a standard segue from each tableviewcell without needing any condition/check. Was a good exercise anyway though :)