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.
Related
I'm trying to pass some data to my view controller class like this:
MyViewController *vc = [[MyViewController alloc] init];
vc.myProperty = dataToBePassed;
[self.navigationController pushViewController:vc animated:YES];
I need to make some view configuring in viewDidLoad, but it seems that viewDidload called earlier than property assignment.
Then in MyViewController implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.myProperty); // Here i get myProperty = nil
}
- (void)viewWillAppear
{
[super viewWillAppear];
NSLog(#"%#", self.myProperty); // Here i get myProperty = dataToBePassed but it's to late
}
How can i get passed data in viewDidLoad method without implementing singleton or delegate patterns?
Try doing this
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
#"MainStoryboard" bundle:[NSBundle mainBundle]];
MyViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"storyboardIdentifier"];
vc.myProperty = dataToBePassed;
[self.navigationController pushViewController:vc animated:YES];
You have to set a storyboard identifier first in the storyboard for the view controller.
While the code sample you provide looks technically correct, I'm with #john-elemans in that you need to show more code.
There is something that is referencing the view which causes it to load and therefore causes viewDidLoad to fire prematurely.
In any case, if something (such as your property) is absolutely essential to the correct building of your view structure, I'd put in its own designated initializer, e.g.,
- (id)initWithPhotoDiameter:(CGFloat)diameter
{
self = [super init...]; // some VC initializer that you should call
if (self) {
_photoDiameter = diameter;
}
return self;
}
Notice the use of the backing instance variable _photoDiameter instead of self.photoDiameter. This is about the only place in a class where you should use the backing ivar, since self is still in the process of being initialized.
Technically there are two approaches that are quite common for lifecycle handling of view controllers related to an application.
Using XIBs
When using XIBs one of the most common if not the most common process to create and setup your view controllers is done programmatically. Following this process, when you initialise the view controller you have the option of either overriding your init method in order for your view controller to have the information prior to loading the view and easing up the process of adjusting drawn content. You can also create a method within your view controller to be called in which you pass the data to be used by the view controller.
Using Storyboard
If you are using storyboards I recommend that you trust segues setup through it. I have found that they make life easier and it will allow you to use certain methods to handle the transition. One of those is prepareForSegue:sender: Within that method I have found that it is easier to setup a view controller after it's initialized accessing the destination controller. You also might consider having all data there before viewDidLoad hence following the segue approach.
I have multiple UIViewController objects within one main UIViewController. I need to call FVC method when I click the main view controller button. Here three view controllers having three separate class files.
From your first controller on didSelectRowAtIndexpath method,
UIStoryboard * board = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController * cntrl = [board instantiateViewControllerWithIdentifier:#"ViewController"];
[self presentViewController:cntrl animated:YES completion:^{
}];
add above code. Here ViewController is nothing but a your Second view controller. Using reference of cntrl pass data to Second controller.
When you want to navigate to any other controller without having navigation controller reference , you use present view controller.
If you are trying to call a method present in other ViewController without presenting it, I guess you are doing it wrong, because that method belongs that ViewController class and ideally should be called when that ViewController's lifecycle is in progress.
For your scenario, I suggest that you should create a utility class, move that method which accepts two strings and then processes something in that utility class and then call that method from your ViewController1 something like :
[UtilityClassName yourMethodWithFirstString : str1 andSecondString : str2];
Hope that clears.
UIViewController *viewVC = [[UIViewController alloc] init];
[self presentViewController:viewVC animated:YES completion:nil];
//Loading a view controller from the storyboard
UIViewController *viewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"IDENTIFIER_OF_YOUR_VIEWCONTROLLER"];
[self presentViewController:viewVC animated:YES completion:nil];
In First View Controller
//Do this inside your btnCall method
SecondViewController * cntrl = [self.storyboard instantiateViewControllerWithIdentifier:#"secondViewControllerIdentifier"];
[cntrl methodName:firstParameter:secondParameter];
In SecondViewController
In .h file
-(void)methodName:firstParameter:secondParameter;
In .m file
-(void)methodName:firstParameter:secondParameter{
//Do your task here
}
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];
}
I have two views in the Tab Bar View. I want my second view to inform first view, when its Text Fields have changed value. I've done all the necessary coding for it, but there is one problem - first view doesn't see connected label outlets in the method declaration.
Code of the second view:
- (IBAction)textFieldHasChanged:(UITextField *)sender {
id<HPAAddCarOverallInfoTVCDelegate> strongDelegate = [[HPAAddCarMainViewController alloc] init];
if([strongDelegate respondsToSelector:#selector(addCarOverallInfoVC:textFieldValueChanged:)]) {
[strongDelegate addCarOverallInfoVC:self textFieldValueChanged:sender.text];
}
}
Code of the first view:
-(void)addCarOverallInfoVC:(HPAAddCarOverallInfoTableViewController *)viewController textFieldValueChanged:(NSString *)value
{
self.overallVCFieldCount.text = value;
NSLog(#"%#", value);
}
self.overallVCFieldCount.text = value; - value exist, but textField doesn't.
As I think, problem belongs at this line of code:
id<HPAAddCarOverallInfoTVCDelegate> strongDelegate = [[HPAAddCarMainViewController alloc] init];
I guess, that delegate isn't exact view with which I am working with. Bouth views are loaded at the same time via storyboard. If I am correct with my thought, can you tell me please, how can I give a pointer to exact first view which as second view are loaded when Tab Bar View controller goes on the screen?
You're creating a new view controller in textFieldHasChanged. If you have that view controller in IB, instantiate it like this:
UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
id<HPAAddCarOverallInfoTVCDelegate> strongDelegate = st instantiateViewControllerWithIdentifier:#"identifier"];
Where identifier is the identifier you have given your view controller in your storyboard.
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: