Xcode - Pass property to view controller in storyboard - ios

I would like to pass properties to a view controller created from a container view in a storyboard. The problem is that I don't know how I could do it.
The blue rectangle is the area where I have two containers view, each one referring to a view controller (one is the custom table view controller at the top and I am interested with and the other one to the view controller below).
The problem is that my custom table view controller needs some properties. I would like to pass the properties from the class where I instantiate the Storyboard (the storyboard is called from an toher class). The view controller containing the container views is instantiated like shown below:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Phenotype" bundle:nil];
GeneralViewController *vc = [sb instantiateViewControllerWithIdentifier:#"phenotype"];
[vc setProperty:property]; // I would like to pass this property to the custom table view controller.
[self.navigationController pushViewController:vc animated:YES];
I try to get the table view controller from the class where I instantiate the stroyboard to set the property at this time
TableViewController *tablevc = [sb instantiateViewControllerWithIdentifier:#"table"];
[tablevc setProperty:property];
...but without any success.
Would anyone have an idea how I can access the property from the custom table view controller directly from the view controller which is instantiating the storyboard?
Thanks for your help,

Select your embed segue from blue rectangle to the tableView on top and give it a segue identifier :) lets say segue identifier is "abcd".
In your ViewController having container write
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "abcd" {
let tableView : YourTableViewClass = segue.destination as! YourTableViewClass
//pass whatever params want :)
}
}
EDIT
Embedded segues behaves just like other segues with one exception that other segues you will have to execute them either programmatically or triggering some event where as embedded segue gets executed automatically when a container loads :)
So you can have segue identifier, write code in prepareForSegue just like you do it for other segues :)

Related

Show segue inexplicably changes to a modal segue when a container view controller is used

I don't know the exact cause of this but I am guessing it has something to do with a container view controller being used. I am actually using several containers, one is for a SWRevealViewController, which houses a generic UIViewController for its rearViewController and a UINavigationController for the frontViewController. The root view controller of this navigation controller is another container view controller, a MainViewController, which contains a UIPageViewController. Page view controller has 3 UITableViewControllers and when I try to navigate from those table view controllers to the relevant view controllers (via a segue), and I want it to be a push transition, it makes a modal transition so what gives?
It is something to do with hierarchy I guess, but it is kinda messed up with all these view controllers right now and I think I broke my brain at some point. I would really appreciate if someone who knows what is wrong can explain.
Thanks for the replies but I got it working without the use of segues and the storyboard, by pushing the view controller in tableView delegate manually, like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
PlaceViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"placeViewController"];
vc.place = self.places[indexPath.row];
[self.navigationController pushViewController:vc animated:YES];
}
was stuck with the same problem and tried to solve for almost 2 days, and finally solved that..
View Hierarchy:
Container View
First Controller (direct child of container)
Second Controller (direct child of container)
Third Controller
Fourth Controller
Solution
added Navigation controller to First Controller
added Navigation Controller to Second Controller
and when want to display any controller instead of presenting the controller itself present the UINavigationController, this will solve the problem of not happening segue push animation...
sample code, hoping it will help:
import UIKit
class ViewController: UIViewController {
var loginViewNavigation : UINavigationController!
var singupViewNavigation : UINavigationController!
override func viewDidLoad() {
super.viewDidLoad()
var storyBoard = UIStoryboard(name: "User", bundle: nil)
loginViewNavigation = storyBoard.instantiateViewControllerWithIdentifier("LoginViewNavigation") as! UINavigationController
singupViewNavigation = storyBoard.instantiateViewControllerWithIdentifier("SignupViewNavigation") as! UINavigationController
self.addChildViewController(loginViewNavigation)
self.addChildViewController(singupViewNavigation)
self.view.addSubview(loginViewNavigation.view)
}
}
Point to observe is the use the UINavigationController of the controller you want to display instead of that ViewController

Calling specific constructor when loading a view controller from segue

I have added a segue between two view controllers using XCode 6 interface builder. However it calls the default constructor. How can I get it to call a specific constructor for the second view?
view 1 -> click button -> activates segue -> calls constructor of view 2 -> displays view 2
Using a segue this is unfortunately not possible, you can't further specify how the destinationViewController should be instantiated.
However, instead of using the Storyboard segue, you can instantiate the UIViewController yourself and just push it manually onto the navigation stack.
- (IBAction)buttonTap
{
ViewController2 *vc2 = <your custom constructor>;
[self.navigationController pushViewController:vc2 animated:YES];
}
(note that this mimics the exact same behaviour that the push segue gives you)
Otherwise, if you want to keep on using the Storyboard segue and your actual goal is to initialize certain properties of ViewController2, you can implemented prepareForSegue: and set the properties there.
- (void)prepareForSegue:(UIStoryboardSegue *)segue
{
ViewController2 *vc2 = segue.destinationViewController;
// set properties here
vc2.prop = xyz;
}
(note the code is not tested but it should convey the main ideas, let me know if you need further explanation)

Passing data between navigation controller and tab controller [duplicate]

This question already has answers here:
Pass data through view controller, tab bar controller, navigation controller and to view controller
(2 answers)
Closed 8 years ago.
If a storyboard contains a view in a navigation controller that segues to another view controller already embedded in a tab controller how can a variable be passed from the initial view to the first view in a tab controller?
Specifically, I am looking to pass a variable using a basic performSegueWithIdentifier approach. By connecting the first view controller (embedded in a navigation controller) to the tab controller, I am unable to pass the data. However, if the connection is made directly between the first view controller and the destination view controller, the destination is no longer embedded in the tab.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "internalItemDetail" {
let destination = segue.destinationViewController as InternalInventoryDetail
let index = itemTable.indexPathForSelectedRow()!
destination.fmRecordId = sectionedItemArray[index.section][index.row]["fmRecordId"] as String!
}
}
To be clear, the goal is to pass a variable from a navigation controller into another view which is embedded in a tab controller while the UI would be showing a tab controller inside a navigation controller.
The UIStoryBoardSegue has a destinationViewController property that will give you the target view controller- in your case a UITabBarController (if I'm reading your question correctly). You'll have to cast it to a UITabBarController and from there you can access its subviews. (Code in objective c but should be easy to convert, I've just never used swift)
UITabBarController *tabControl = (UITabBarController *)segue.destinationViewController;
MyViewController *mv = (MyViewController *)tabControl.viewControllers[0];// whatever index you want

Go to another Viewcontroller

In my app, I need to go to another UIViewController with a button click, but when I did it in the new UIViewController it displays only what I set programmatically.
I used:
NewSubject *NewS = [[NewSubject alloc] initWithNibName:nil bundle:nil];
[self presentViewController:NewS animated:YES completion:nil];
"NewSubject" is the UIViewController I need to go too, however I want the computer to display also the stuff I set by the Storyboard.
Have you set in the Storyboard, in the NewSubject View Controller, in the third tab (Show Identity Inspector) the StoryBoard ID?
You should set it to some name, such as "NewSubject" and use it as follow:
NewSubject *NewS = [self.storyboard instantiateViewControllerWithIdentifier:#"NewSubject"];
[self.navigationController pushViewController:NewS animated:YES];
I want the computer to display also the stuff I set by the Storyboard.
If you're using a storyboard, -initWithNibName:bundle: is the wrong method to use. You can use UIStoryboard's -instantiateViewControllerWithIdentifier: method to create a new view controller that's defined in a storyboard, but the more typical approach is to have your button trigger a segue between the two view controllers.
Try this:
While editing your storyboard, control-drag from your button to the new view controller. A popup menu should appear that lets you choose how you want to transition between the view controllers -- push (push the new controller onto the top of the navigation stack), modal (present the view controller modally), etc. Pick the appropriate one.
In simple cases, you're done -- there's no need to write any code just to get the transition to happen. The segue takes care of creating the new view controller and performing the transition for you. However, you often want to pass some data from the existing view controller to the new one. If that's the case, implement -prepareForSegue:sender: in the existing view controller -- this method gives you a chance to pass whatever data you need. It'll look something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// It doesn't hurt to check that it's the segue that you expect
if ([segue.identifier isEqualToString:#"MySegueIdentifier"]) {
NewViewController *newVC = segue.destinationViewController;
// This is your chance to set properties or call methods to pass data to the new view controller
newVC.foo = self.foo;
newVC.bar = self.bar;
}
}

Creating a segue programmatically

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()
}
...
}

Resources