Most of the information I found involving implementing protocols and delegates involves a step where you do this;
DestinationViewController *destinationVC = [[destinationViewController alloc] init];
destinationVC.delegate = self;
But after hours of frustration because I couldn't get it to work I finally stumbled across another way to allocate the destinationVC in prepareForSegue
DestinationViewController *destinationVC = segue.destinationViewController;
destinationVC.delegate = self;
Which actually works. What was I doing wrong? It seemed using the first method my delegate was never set to self.
When instantiated from a storyboard, the initWithCoder: methid is called, not the init method.
DestinationViewController *destinationVC = [[destinationViewController alloc] init];
destinationVC.delegate = self;
is how you do when your controller is not from a storyboard: you init it from the code. After that you have to manually handle the transition from your source VC to your destination VC.
DestinationViewController *destinationVC = segue.destinationViewController;
destinationVC.delegate = self;
is how you do when your controller is defined in a storyboard and is the destination of a segue.
When you perform a segue, the prepareForSegue: method of the source view controller is called, in which you should configure your destination like you want: setting properties, delegates, passing data,...
There is two way you can pushController while using UIStoryboard.
Option 1 : taking reference of actually UIViewController from storyboard.
UIViewController *displayTable = [self.storyboard instantiateViewControllerWithIdentifier:#"nextViewcontroller"];
[self.navigationController pushViewController:displayTable animated:YES];
Option 2 : Using Segue
[self performSegueWithIdentifier:#"MySegue" sender:sender];
In your first case you are allocating object and assign delegate. that does't means while performing pushViewController operation same reference is passing. so in that case two different reference is created. so you delegate is point out some other reference that doesn't exist.
may this help you.
Here is the basic tutorial about segues, updated for Xcode 6 and above.
https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardSegue.html
When you use storyboards, all necessary context provided by UIStoryboardSegue class. it holds destination view controller for you. So, you must access destination controller throw destinationViewController property.
if you want to manually add controller to your navigation stack:
{
// binds your viewController from storyboard with local instance
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"YOUR_STORYBOARD_IDENTIFIER"];
// set your delegate
vc.delegate = self;
// push controller into navigation stack
[self.navigationController pushViewController:vc animated:YES];
}
Related
I am using this to implement ContainerViewContoller. Everything is going fine but I'm unable to pass data from one child ViewController to an other child ViewContoller.
CouponCodeViewController *couponVC = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"CouponCodeViewController"];
couponVC.coponcode=#"this is data";
couponVC.title = #"Enter Coupon Code";
[couponVC viewWillAppear:true];
CategoryViewController *categoryVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CategoryViewController"];
categoryVC.title = #"Choose A Category";
YSLContainerViewController *containerVC =
[[YSLContainerViewController alloc]
initWithControllers:#[couponVC,categoryVC]
topBarHeight:statusHeight + navigationHeight
parentViewController:self];
When I call my container ViewController it loads all childVC at the same time. Now I want to pass data on click from CouponCodeViewController to CategoryViewController but I'm not able to do so because viewDidLoad, viewWillappear, and viewDidAppear are not called
CouponCodeViewController I am going using this:
- (IBAction)skipAct:(id)sender {
// CategoryViewController *category=[[CategoryViewController alloc]init];
// category.userInput=#"this is the data";
[self.scrollMenuDelegate scrollMenuViewSelectedIndex:1];
}
#pragma mark -- YSLContainerViewControllerDelegate
- (void)containerViewItemIndex:(NSInteger)index currentController:(UIViewController *)controller
{
[controller viewWillAppear:YES];
}
In CategoryViewController my viewDidLoad and viewDidAppear are not called.
How can I pass data from childVC to another ChildVC.
That because when you init all controllers, the parent controller doesn't do 'addChildViewController'. It does that only when showing a new controller (I've looked the code).
And after it move from this controller, it removes it from the controller hierarchy.
I would use either delegate or notification to pass the data.
Another option (which I don't like) is to give the coupons controller a reference from the categoryViewController.
I have 3 different buttons which upon touch present the same UINavigationViewController with a container.
However, each button represents which view controller will be embed at the container.
How can I embed the necessary viewController by code?
what you can do is use an identifier which would be assigned to your various viewController as storyboardID
such as fisrtVC, SecondVC, thirdVC
the depending upon whichButton is pressed just set the identifier and use this identifier when
you want to push the controller such as
for example
while you push the navigation viewController just pass the storyboard Identifier such as
Declare a NSString *identifier;
-(IBAction)firstButtonClicked{
identifier=#"firstVC";
//pass this identifier to your navigationController
}
similarly for other Controllers
When you push the navigation controller make sure to pass this identifier along now depending upon the value you can initiate the controller on you VC as
on ViewDIdApppear:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
NSString* viewType = passedIdentifier
UIViewCOntroller* viewController = [storyboard instantiateViewControllerWithIdentifier:viewType];
Load this "viewCOntroller in your ContainerView"
You should implement prepareForSegue method:
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
id is your button that will fire segue. You do segues by drugging and drop in storyboard. Put an if statement in this method and tell your UIViewController which UIView to load in container. You can pass data like this:
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
Second snippet taken from this answer.
Update.
To load different UIView in container put if statement into viewWillApper method.
This method firing earlier than viewDidLoad. If statement must check some property that tell what UIView to init. You setting up this property in prepareForSegue.
It will look like this:
if (self.viewToLoad == 1)
{
self.dynamicView = MyCustonUIViewNumberOne *view = [MyCustonUIViewNumberOne alloc] init];
}
Update 2.
Or you can do it dynamically like in this answer:
if (self.viewToLoad == 1)
{
// Replacing with your dimensions
CGRect frame = CGRectMake(x, y, width, height);
MyCustonUIViewNumberOne *dynamicView = [[MyCustonUIViewNumberOne alloc] initWithFrame:frame];
[self.container addSubview: dynamicView];
} else {
// Init other view
}
The container property:
I have code that uses Storyboards for seques, like so:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowDiagnosis"])
{
[segue.destinationViewController setHappiness:self.diagnosis];
}
...
But I want to do it programmatically. I have myViewController class and when I click on a button I want to animate and push to myUINavigationController.
How is this done programmatically?
First things first, a segue cannot be created programmatically. It is created by the storyboard runtime when it is time to perform. However you may trigger a segue, which is already defined in the interface builder, by calling performSegueWithIdentifier:.
Other than this, you can provide transitions between view controllers without segue objects, for sure. In the corresponding action method, create your view controller instance, either by allocating programmatically or instantiating from storyboard with its identifier. Then, push it to your navigation controller.
- (void)buttonClicked:(UIButton *)sender
{
MyViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"my-vc-identifier"];
// OR MyViewController *vc = [[MyViewController alloc] init];
// any setup code for *vc
[self.navigationController pushViewController:vc animated:YES];
}
First of all segue can be used only with if you have a UINavigationController that will handle the navigation (push, pop, etc).
So if you have a UINavigationController and you want to push another UIViewController on the stack without using segue then you can use pushViewController:animated: method which also has a reverse popViewControllerAnimated:. Also UINavigationController provides other methods for adding/removing UIVIewControllers, for more info check UINavigationController class reference.
I have an application with two segues. In one of the segues, the current view controller becomes a delegate and the other does not.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"MoreOptions"]) {
UINavigationController *navigationController = segue.destinationViewController;
MoreOptionsViewController *controller = (MoreOptionsViewController *)navigationController.topViewController;
controller.delegate = self;
} else if ([segue.identifier isEqualToString:#"FullStoryView"]) {
SingleStoryViewController *detailViewController = segue.destinationViewController;
detailViewController.urlObject = sender;
}
}
All of this is working fine, but I would like to try and understand the code better. What I don't understand is that I have to get a reference to the MoreOptionsViewController by grabbing it from navigationController.topViewController rather than simply getting it from segue.destinationViewController like I do in the second if condition. Is it because I'm setting the current view controller (self) as the delegate? Again, I'm not trying to solve a problem, just trying to get a better understanding of what's going on.
Take a look at your storyboard and it should be evident why this is the case. You have embedded MoreOptionsViewController in a UINavigationController and connected a segue to the navigation controller, thus making it the destinationViewController. This is fairly common.
The delegate is largely irrelevant in the context of your question.
Your first segue's destination is a navigation controller, which contains the view controller you are really interested in. Therefore to get to that view, you need to go through the navigation controller since that won't have any properties you are interested in setting.
Your second segue goes directly to a single view controller, so you can access it directly.
I was wondering how to properly use the storyboard to put up a view controller modally. Personally I prefer working with xibs, but it seems that the storyboard is gaining popularity and will be the way to go in the future.
The way I would normally put up a view controller modally would be like this: let's say we have ViewControllerA (A for short) and ViewControllerB (B for short).
I would then normally put a protocol in B.h specifying the delegate method when B wants to be dismissed and add the id<theProtocol> delegate field as an assign property. Assuming i'm busy in A and I want to present B modally, I would write:
B* b = [[B alloc] initWithNibName:#"B" bundle:nil];
b.delegate = self;
[self presentModalViewController:B animated:YES];
Using the storyboard, I know it's possible to put up a different view controller in a modal way by ctrl-dragging from a button to a viewcontroller and selecting modal as transition type. I'm just wondering though; where do I set the delegate of the new view controller? What's the correct practice of passing things to your modal view controller? I don't really know what the whole deal with Segues is...
Take a look at this tutorial
According to it, you should set the delegate as follows:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddPlayer"])
{
UINavigationController *navigationController =
segue.destinationViewController;
PlayerDetailsViewController
*playerDetailsViewController =
[[navigationController viewControllers]
objectAtIndex:0];
playerDetailsViewController.delegate = self;
}
}
Where #"AddPlayer" is the name of your 'modal' segue
Instead of using the navigation controller you could directly use the UIStoryboardSegue object passed in prepareForSegue. It has a property called destinationViewController which is the view controller that is being instantiated. I find that a lot cleaner.
This is an example.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddPlayer"])
{
PlayerDetailsViewController
*playerDetailsViewController =
(PlayerDetailsViewController *) segue.destinationViewController;
playerDetailsViewController.delegate = self;
}
}
IMO I think that storyboards are great because they function like a blueprint of your application. Also I've never liked nibs. =D