How to pass Data to a ViewController created using instantiateViewControllerWithIdentifier? - ios

I've created an app that uses a bunch load of Container views. Today i've found out that embedded segues are not supported in iOS 5 (thanks Xcode for letting me know.)
So right now I have a lot of embedded segues that passes data between one another.
I'm using this code to load ViewControllers inside a UIView instead of the Container from the IB:
RootViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
NewsViewController *news = [storyboard instantiateViewControllerWithIdentifier:#"NewsViewControllerID"];
news.view.frame = self.newsSection.bounds;
[self.newsSection addSubview:news.view];
[self addChildViewController:news];
[news didMoveToParentViewController:self];
// Do any additional setup after loading the view.
}
How can I use the above and still pass data to NewsViewController (something like prepare for segue)
I've tried inserting in the above something like
NewsViewController *news = [storyboard instantiateViewControllerWithIdentifier:#"NewsViewControllerID"];
news.view.frame = self.newsSection.bounds;
news.myLabel.text = #"Test";
[self.newsSection addSubview:news.view];
But the label won't change.
I prefer just to pass the data and not use a singleton or delegation.
Thanks

I would recommend creating a public method, from where you could pass the data you need on your NewsViewController controller. Still, the issue right now is that the you are updating the UI before it has been initialised. Also I would recommend you to have a look on how to create content UIViewControllers, because right now you are only adding its UIView. Check this document from Apple, look at the section Implementing a Custom Container View Controller. And this part of the code:
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.frame = [self frameForContentController]; // 2
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self]; // 3
}
So in your case, after the step 3 has been made, you could pass the values you want to use inside your NewsViewController.

The problem could be that at the moment you are setting the text of the label, the label is still nil.
Try adding an NSString property to your NewsViewController (e.g. labelText), and in viewDidLoad do self.myLabel.text = self.labelText;.

you have pass the string your next view controller and viewDidLoad you have to the string.
Its not working because your view not yet created and if check the instance of UIlabel it will nil. So you have set the text in your next viewcontroller when it loaded in the memory.
Edit
you have create a property variable of NSString type(textString) in NewsViewController and pass the string as news.textString = #"Test";
and the in ViewDidLoad of NewsViewController set that string to label as myLabel.text = textString;
Better Explanation
-Declare NSString property in the NewsViewController.h
#property (strong, nonatomic) NSString *assignText;
-Instantiate normally, but assign the string and not the label.
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
NewsViewController *news = [storyboard instantiateViewControllerWithIdentifier:#"NewsViewControllerID"];
news.assignText = #"Test";
[self addChildViewController:news];
-Then in your NewsViewController viewDidload, assign the label to the string.
-(void)viewDidLoad {
[super viewDidLoad];
self.myLabel.text = self.assignText;
}

Related

add UIViewController in subview

I don't know if this is the right key to search "add UIViewController in subview".
As what you can see in my image ,there are two ViewController, the main and the second controller. Inside the main controller there is a UIView(blue background color). Inside in UIView, I want to add the second ViewController in my UIView. I have this code but It didn't work.
here's my code
#import "ViewController.h"
#import "SampleViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIView *testView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
SampleViewController * sample = [[SampleViewController alloc] initWithNibName:#"SampleViewController" bundle:nil];
sample.view.frame = CGRectMake(0, 0, self.testView.bounds.size.width, self.testView.bounds.size.height);
[self.testView addSubview:sample.view];
}
#end
I want to know if this is possible? I know initWithNibName: works in xib file, I don't the exact term to search in google about this. I'm just trying to experiment something if this is possible in IOS. Hoping you understand what I'm trying to do. Hoping for your advice. Thanks in advance
here's my update
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIView *testView;
#property(strong,nonatomic) SampleViewController * samples;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIStoryboard *storyBoard = self.storyboard;
SampleViewController * sample = [storyBoard instantiateViewControllerWithIdentifier:#"SampleViewController"];
// SampleViewController * sample = [[SampleViewController alloc] //initWithNibName:#"SampleViewController" bundle:nil];
[self displayContentController:sample];
//commented the below line because it is not needed here, use it when you want to remove
//child view from parent.
//[self hideContentController:sample];
}
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.bounds = self.testView.bounds; //2
[self.testView addSubview:content.view];
[content didMoveToParentViewController:self]; // 3
}
- (void) hideContentController: (UIViewController*) content
{
[content willMoveToParentViewController:nil]; // 1
[content.view removeFromSuperview]; // 2
[content removeFromParentViewController]; // 3
}
I always get this error
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </Users/ace/Library/Developer/CoreSimulator/Devices/035D6DD6-B6A5-4213-9FCA-ECE06ED837EC/data/Containers/Bundle/Application/EB07DD14-A6FF-4CF5-A369-45D6DBD7C0ED/Addsubviewcontroller.app> (loaded)' with name 'SampleViewController''
I think, its looking for a nib. I didn't implement a nib here.
You should use Child containment concept, here MainViewController is a parent view controller and you want to add child view controller view as a subview on Main View Controller.
Adding and Removing a Child
//call displayContentController to add SampleViewCOntroller view to mainViewcontroller
[self displayContentController:sampleVCObject];
// write this method in MainViewController
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.bounds = testView.bounds; //2
[testView addSubview:content.view];
[content didMoveToParentViewController:self]; // 3
}
Here’s what the code does:
It calls the container’s addChildViewController: method to add the child. Calling the addChildViewController: method also calls the child’s willMoveToParentViewController: method automatically.
It accesses the child’s view property to retrieve the view and adds it to its own view hierarchy. The container sets the child’s size and position before adding the view; containers always choose where the child’s content appears. Although this example does this by explicitly setting the frame, you could also use layout constraints to determine the view’s position.
It explicitly calls the child’s didMoveToParentViewController: method to signal that the operation is complete.
//you can also write this method in MainViewController to remove the child VC you added before.
- (void) hideContentController: (UIViewController*) content
{
[content willMoveToParentViewController:nil]; // 1
[content.view removeFromSuperview]; // 2
[content removeFromParentViewController]; // 3
}
For more details, please refer to apple doc:
https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html
Configuring a Container in Interface Builder, for those who don't want to write code.
To create a parent-child container relationship at design time, add a container view object to your storyboard scene, as shown in Figure 5-3. A container view object is a placeholder object that represents the contents of a child view controller. Use that view to size and position the child’s root view in relation to the other views in the container.
When you load a view controller with one or more container views, Interface Builder also loads the child view controllers associated with those views. The children must be instantiated at the same time as the parent so that the appropriate parent-child relationships can be created.
You can do this simply by using StoryBoards
Open storyboards and select the view controller in which your Blue view is present, open Utilities search for ContainerView and drag it into your blue view, this will automatically adds an view controller that acts as child view for your view. You can resize your container view in size inspector.
I've resolved the problem about the uiview on second uiviewcontroller.
Follow the code that I've used:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SecondViewController *sb = (SecondViewController *)[storyboard instantiateViewControllerWithIdentifier:#"sb"];
sb.view.backgroundColor = [UIColor redColor];
[sb willMoveToParentViewController:self];
[self.view addSubview:sb.view];
[self addChildViewController:sb];
[sb didMoveToParentViewController:self];
I hope to help you.
Thanks a lot
SampleViewController * sample = [[SampleViewController alloc] initWithNibName:#"SampleViewController" bundle:nil];
sample.view.frame = CGRectMake(0, 0, self.testView.bounds.size.width, self.testView.bounds.size.height);
[self addChildViewController:sample];
[self.testView addSubview:sample.view];
You can add a childViewController to UIViewController since iOS5..
it is a great way to maker smaller, more reusable viewControllers, I also really like it.
you're really close, but you just need a couple more lines of code..
///
[self addChildViewController:sample];
[self.testView addSubview:sample.view]; //you already have this..
[sample didMoveToParentViewController:self];
In you viewWillDisappear: or one of the other teardown methods you'll need to clean up like this:
//we'll need another pointer to sample, make it an iVar / property..
[sample willMoveToParentViewController:nil]; // 1
[sample removeFromSuperview]; // 2
[sample removeFromParentViewController]; // 3
You can read the Apple docs on containing child viewControllers here https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
I'm tring to adding UIViewController in subview, and this work.
On UIViewController I've added 2 button, in .h file:
-(IBAction)matchButtonAction:(id)sender;
-(IBAction)closeButtonAction:(id)sender;
And in .m file:
-(IBAction)matchButtonAction:(id)sender
{
NSLog(#"matchButtonAction");
}
-(IBAction)closeButtonAction:(id)sender
{
NSLog(#"closeButtonAction");
}
but I don't see the log.
I need to add same parameter on UIViewController instantiateViewControllerWithIdentifier?
How to resolve the problem?
My UIViewController initialization is:
AlertDialogViewController *alertDialogView = (AlertDialogViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"alertDialogView"];
[alertDialogView willMoveToParentViewController:self];
[viewController.view addSubview:alertDialogView.view];
[viewController addChildViewController:alertDialogView];
[alertDialogView didMoveToParentViewController:viewController];
Since your view controller is in storyboard you should use instantiateViewControllerWithIdentifier to get the VC from storyboard.
SampleViewController * sample = [self.storyboard instantiateViewControllerWithIdentifier:#"IdOfSampleViewController"];
sample.view.frame = CGRectMake(0, 0, self.testView.bounds.size.width, self.testView.bounds.size.height);
[self.testView addSubview:sample.view];
Don't forget the add the identifier for the SampleViewController in storyboard
I've try to use this code:
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
secondViewController.view.frame = CGRectMake(0, 0, self.mySubView.bounds.size.width, self.mySubView.bounds.size.height);
[self addChildViewController:secondViewController];
[self.viewDialogSolution addSubview:secondViewController.view];
but I see only myview not the layout of secondViewController.
Ho to resolve the probleam?
Thanks a lot
let controller:SecondViewController =
self.storyboard!.instantiateViewController(withIdentifier: "secondViewController") as!
SecondViewController
controller.view.frame = self.view.bounds
controller.willMove(toParent: self)
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)

What is the accepted way to set UI properties when pushing a new VC?

Let's say you're pushing a new View Controller onto the navigation stack and need to set a UI property (ex: a UI label's text or something). When you initialize the VC, it's views are not set (so they could be nil). Therefore, setting properties won't work. For example:
SomeViewController *vc = [[SomeViewController alloc] init];
SomeViewController.someUILabel.text = #"foo";
[self.navigationController pushViewController:vc animated:YES];
This won't set the UI label's text because vc.view and its subviews are nil. A couple of ways to remedy this are:
After calling init on the VC, do something like [vc view] which will load the view and then allow you to set properties.
Set a non-UI ivar and then in viewDidLoad set up the UI like so:
SomeViewController *vc = [[SomeViewController alloc] init];
SomeViewController.uiTextLabel = #"foo";
[self.navigationController pushViewController:vc animated:YES];
// in viewDidLoad
self.someUILabel.text = self.uiTextLabel
Is there an accepted way to get rid of this problem? Is one of these better than the other or is there a different solution?
You should not set the label value from another view controler. A controller controls its views.
You should have a NSString property in you SomeViewController, and set that public property instead with the string you want. Then, in SomeViewController viewDidLoad method set the value of the label to the one in the property.
Initialize the views after pushing it in the stack and in the pushed vc not the one that pushed it.
I found that the most elegant method to do this is:
in the main VC:
SomeViewController *vc = [[SomeViewController alloc] init];
SomeViewController.textForLabel = #"foo";
[self.navigationController pushViewController:vc animated:YES];
And in the pushed VC:
#interface SomeViewController : UIViewController
#property (nonatomic, strong) UILabel *textLabel;
#end
- (void)setTextForLabel:textForLabel
{
_textForLabel = textForLabel;
self.textLabel.text = self.textForLabel;
}
- (void)viewDidLoad
{
self.textLabel.text = self.textForLabel;
}
So basically you set the property as an NSString and then adjust the UI in two separate places.

iOS: data disappear after passing it to another ViewController

I have a problem in my app.
When I pass a UIView to a SecondViewController in this way
SecondViewController *second = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
second.currentView = currentView;
(second.currentView is my property in SecondViewController and I pass to it my view)
At this point, it's all ok and my SecondViewController show correctly my currentView, but when I retund in FirstViewController my currentView disappear!!!
Why? Can you help me? thanks
UPDATE
in FirstViewController.h
IBOutlet UIView *currentView;
in FirstViewController.m
SecondViewController *second = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
second.currentView = currentView;
[self presentViewController:second animated:NO completion:nil];
in SecondViewController.h
#property (nonatomic, strong) UIView *currentView;
in SecondViewController.m
- (void) viewDidAppear:(BOOL)animated{
[self.view addSubview:self.currentView];
self.currentView.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
}
this is my code in the two ViewControllers, and when I return in FirstViewController currentView there isn't... it disappear...
A view only have super view, so when you add the view to SecondViewController you care adding it to its view hierarchy and thus removing to from your current view controller. When you call something like:
- (void) viewDidLoad{
[super viewDidLoad];
// Here the view is removed the first view and placed on the second view.
[self.view addSubView:self.secondView];
}
Thus if you want to add it back in the first view you need to add it again to view hierarchy of the first view.
A beter solution would be pass the data to the next view controller, not to view but the model. Like pass the model object holding the data presented in the view.
I solved my issue!!!
When I return in FirtsViewController I must add a new time my view in my self.view at last position, in this way:
[self.view addSubview:currentView];
[currentView setCenter:initially_center_view];
in this way I see my view!!!

Passing id from Parse table view to a detail view

(I know this has been asked before, unfortunately none of the related StackOverflow answers I've looked through and tried out while trying to figure this problem out have worked for me. Any help would be much appreciated.)
I'm building a simple news app. The opening screen grabs the latest news entry using Parse's API.
And when you tap the menu button, you get a UITableView of past news days:
I want to be able to pass the id from the selected Parse table cell back to the main view.
I'm using ECSlidingViewController for the sliding menu and a Storyboard for the various screens:
But because of the way I'm using ECSlidingViewController, I can't figure out how to pass data via segues like I otherwise would be able to, so I'm trying to just pass it via the view controllers with a custom init.
Here's my MainViewController.h code:
#interface MainViewController : UIViewController
#property (nonatomic) NSString *detailId;
- (id)initWithDetailId:(NSString*)theId;
#end
Here's my didSelectRowAtIndexPath code in MenuViewController.m:
MainViewController *mainVC = [[MainViewController alloc] initWithDetailId:#"test"];
[self.navigationController pushViewController:mainVC animated:YES];
Here's MainViewController.m's initWithDetailId:
- (id)initWithDetailId:(NSString*)theId
{
NSLog(#"initWithDetailId");
self = [super initWithNibName:nil bundle:nil];
if (self) {
detailId = theId;
}
return self;
}
And here's MainViewController.m's viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"detailId equals: %#", self.detailId);
}
But all I get from the NSLog is "detailId equals: (null)". What am I doing wrong here? Thanks!
you have forgot to put self. in front of detailId = theId;
self.detailId = theId;
that is
- (id)initWithDetailId:(NSString*)theId
{
NSLog(#"initWithDetailId");
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.detailId = theId;
}
return self;
}
there is one another method of doing this:
MainViewController *mainVC = [self.storyBoard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
mainVc.detailId=#"test";
[self.navigationController pushViewController:mainVC animated:YES];

How do you programmatically call a viewcontroller get its view in the storyboard?

I am using a BookController class which is using pagenumbers to keep track of the current view. Currently I am creating each view controller on demand and writing the code programmatically. I would like to access the view controllers that I have created in the StoryBoard (the xib files) so that when I demand a new page it will access a Second view controller I have created.
// Provide a view controller on demand for the given page number
- (id) viewControllerForPage: (int) pageNumber
{
if ((pageNumber < 0) || (pageNumber > 31)) return nil;
if(pageNumber == 0){
//here is where I want to access the entire xib file that the SecondViewController is connected with
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
SecondViewController *myVC = (SecondViewController *)[storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
myVC = [BookController rotatableViewController];
return myVC;
}
else if(pageNumber == 1){
// Establish a new controller
UIViewController *controller = [BookController rotatableViewController];
// Add a text view
UITextView *textview = [[UITextView alloc] initWithFrame:(CGRect){.size = CGSizeMake(100.0f,100.0f)}];
textview.text = [NSString stringWithFormat:#"This is dedicated to people"];
textview.font = [UIFont fontWithName:#"Futura" size:18.0f];
textview.center = CGPointMake(475.0f, 700.0f);
[controller.view addSubview:textview];
// Add a label
UILabel *textLabel = [[UILabel alloc] initWithFrame:(CGRect){.size = CGSizeMake(200.0f, 200.0f)}];
textLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
textLabel.text = [NSString stringWithFormat:#"1"];
textLabel.font = [UIFont fontWithName:#"Futura" size:18.0f];
textLabel.center = CGPointMake(475.0f, 985.0f);
[controller.view addSubview:textLabel];
// Add it as an image
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon#2x.png"]];
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageView.center = CGPointMake(160.0f, 230.0f);
[controller.view addSubview:imageView];
return controller;
}
Just not sure how to make a call to access that xib file i've created and make it into the first page (page=0). The second page (page =1) is an example of how i have drawn all the other pages in my book programmatically. Thanks!
Remember the Storyboard is just a collection of NIBs which simply instantiate the hierarchy of each view and connect the outlets to the owning view controllers. You do not want to instantiate the Storyboard yourself to just create a single view controller. What that is doing is creating new instances when the application has already been launched and is running with different instances. Even if you did have them wired up they would be wired to instances which are redundant and not the actual instances you want.
What I would do instead is create an individual NIB file for SecondViewController which you will use separately. Then you will need to wire it together. If this code is within the instance you need to access you would simply pass it along to a property on SecondViewController. Or maybe you just pass along values but most likely you will want to set a delegate property and define a protocol for SecondViewController to call back to the instance which created it.
For your code you can simply load the NIB with the following code.
SecondViewController *vc = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
vc.delegate = self;
You just need to define that delegate and possibly any properties you need to give data to the newly created view controller.
Below is an example of a delegate setup which I recently created for a SideBar interface using a Storyboard. I have a container view for the Header VC which is in the Home VC. This Header VC could be like your SecondViewController because I could not connect it in the Storyboard so I did it with code. First I created a delegate property on the Header VC.
#protocol IFHeaderDelegate;
#interface IFHeaderViewController : UIViewController
#property (nonatomic, assign) IBOutlet id<IFHeaderDelegate> delegate;
#end
#protocol IFHeaderDelegate <NSObject>
- (void)headerViewDidToggleSideBar:(IFHeaderViewController *)sender;
#end
Then when a button is tapped I use the delegate for the callback. (Notice I use an NSAssert to verify the delegate is defined just to give me a heads up if I missed it.)
#import "IFHeaderViewController.h"
#interface IFHeaderViewController ()
#end
#implementation IFHeaderViewController
- (IBAction)siderBarButtonTapped:(id)sender {
NSAssert(self.delegate != nil, #"Delegate must be defined!");
if (self.delegate != nil) {
[self.delegate headerViewDidToggleSideBar:self];
}
}
#end
But in order to wire it up I had to set the delegate from the Home VC which I could not do from the Storyboard. What I did was set it in the Home VC when the embed segue was fired in prepareForSegue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DebugLog(#"segue.identifier: %#", segue.identifier);
if ([#"HomeHeader" isEqualToString:segue.identifier]) {
NSAssert([segue.destinationViewController isKindOfClass:[IFHeaderViewController class]], #"Destination VC must be the Header VC");
IFHeaderViewController *headerVC = (IFHeaderViewController *)segue.destinationViewController;
headerVC.delegate = self;
}
}
You can find the full project on GitHub: https://github.com/brennanMKE/Interfaces/tree/master/SideBar

Resources