I am trying to set up a project which dynamically creates ViewControllers based off a JSON file. I asked a question the other day about how to create this here
xCode Dynamically create ViewControllers
So I have been able to create the ViewControllers dynamically and after playing around with it more, I have been quite easily set it up so when the app loads, it generates how ever many numbers of ViewControllers is specified in the JSON. I can also navigate between them easy enough and also have each screen set up buttons, views etc from the JSON. I do this simply by doing
NSDictionary *MainJSON = [sJson JSONValue];
NSArray *theArray = [MainJSON valueForKey:#"viewControllers"];
Then loop through theArray creating a ViewController instance.
My problem is, I want to be able to pass data between the ViewControllers. So say for example I set it up in the JSON so the first ViewController has a text field. The user will enter some data and I want to be able to set it up so when they navigate to the next screen, that data is carried to the next screen. I currently switch screens like this
-(void)nextScreen
{
for(int i = 0; i < [vcArray count]; i++)
{
int iID = i+1;
if([self.navigationController.viewControllers count] == iID)
{
ViewController *theVC = [vcArray objectAtIndex:i];
[self.navigationController pushViewController:theVC animated:YES];
break;
}
}
}
Now usually each ViewController would have a class, so in the ViewController I am pushing I can do property and synthesise a NSString in that class, so when passing data to the next ViewController, I would just do this.
vc.theString = textField.text
But as I am dynamically generating these ViewControllers, I can't do this as I am only using one .h and .m file and each ViewController I generate is using the same XIB file. Does anyone have any ideas of how I could go about passing data in this set up?
If your problem is that you need different xib for same view controller, you can supply all of them in the main iOS app, later by passing xib name in JSON you can instantiate that xib and assign to the controller.
If you also need to recall UIView objects you don't know by name, you can always use the tag attribute stored in JSON as well.
Maybe you can also store the UIView type, such as 'UILabel', 'UITextView', ecc... and call appropriate setter for value.
Figured it out. If I give each view a tag from the JSON, can easily connect up the objects I need.
Related
Hi I have a uiviewcontroller that adds 4 childviews after receiving data from a webservice. I add an observer to the uiviewcontroller that watches when the data is completely loaded from the webservice. Once it is, the uiviewcontroler adds 4 childviews.
however my problem is it doesn't do this unless i switch to another tab view than go back to that tab again.
Is there a way to reload this page? I used [uiviewcontroller.view setNeedsDisplay] under the function that responds to the notification from the observer but the view doesn't refresh.
thanks for your guys' help :)
here is my observer function:
-(void)projectListFromServer:(NSNotification *) response
{
NSLog(#"%#",response);
for (int i = 0; i < 4; i++)
{
childview2 = [self.storyboard instantiateViewControllerWithIdentifier:(#"slider")];
childview2.image = #"wicked";
childview2.couponID = #"cool";
NSLog(#"%#", self.childview2.couponID);
[self addChildViewController:childview2];
}
[self.childview.view setNeedsDisplay];
}
You are calling addChildViewController, but then it looks like you aren't actually adding childview2's view hierarchy to this containing view controller's view hierarchy.
You'll need to do something like this, adjusted of course depending on where in your current hierarchy you actually want these new views to be inserted:
[self addChildViewController:self.childview2]; // assuming childview2 is a #property
[self.view addSubview:self.childview2.view]; // adjust based on your actual hierarchy
[self.childview2 didMoveToParentViewController:self];
BTW, you should avoid giving view controllers names like childview2. Views and view controllers are two completely different things.
iOS 5 adds a nice feature allowing you to nest UIViewControllers. Using this pattern it was easy for me to create a custom alert view -- I created a semi-transparent view to darken the screen and a custom view with some widgets in it that I could interact with. I added the VC as a child of the VC in which I wanted it to display, then added its views as subviews and did a little animation to bring it on the screen.
Unfortunately, I need to support iOS 4.3. Can something like this be done, or do I have to manage my "alert" directly from the VC in which I want to display it?
MORE INFO
So if I create a custom view in a nib whose file owner is "TapView" and who has a child view that is a UIButton. I tie the UIButton action to a IBAction in TapView.
Now in my MainControllerView I simple add the TapView:
TapView *tapView = [[TapView alloc] init];
[[self view] addSubview:tapView];
I see my TapView, but I can't interact with the UIButton on it and can interact with a UIButton on the MainControllerView hidden behind it. For some reason I am not figuring out what I'm missing...
Not sure if this helps, but, in situations where I've needed more control over potential several controllers, I've implemented a pattern where I have a "master" controller object (doesn't need to be descendent from UIViewController), which implements a delegate protocol (declared separately in it's own file), and then have whatever other controllers I need to hook into declare an object of that type as a delegate, and the master can do whatever it needs to do in response to messages from the controllers with the delegate, at whatever point you need; in your case, that being displaying the alert and acting as it's delegate to handle the button selection. I find this approach to be very effective, simpler and usually cleaner. YMMV ;-)
Regd. your second query, where you are trying to create a custom view using nib. Don't change the FileOwner type, instead set "TapView" for the class property of the top level view object.
Once you have done this, you might experience difficulty when making connections. For that just manually choose the TapView file for making connections.
Also, to load the view you need to load its nib file. For which you can create a class level helper method in TapView class like below
+(TapView *) getInstance
{
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"TapView" owner:self options:nil];
TapView *view;
for (id object in bundle) {
if ([object isKindOfClass:[TapView class]]) {
view = (TapView *) object;
break;
}
}
return view;
}
Now, you get a refrence to you view object like this
TapView *tapView = [TapView getInstance];
I am stuck trying to set and retrieve the text in a UITextField object on titleViewController.
I have a Journal object that has a reference to the titleViewController object, but I can not seem to set the text value for the UITextField.
I have an outlet to the UITextField object in the titleViewController, and Journal can see it, but when I write to code to set the value nothing happens when it is run.
There is no errors that pop up in XCode or the Log. I know it should work as the set up works for accessing custom methods in other such viewControllers being managed by the same Journal.
-EDIT-
Here is a code sample as requested:
//Get viewController from Array
TitleVC *titlePage = [_PageArray objectAtIndex:0];
//Get string from Dictionary
NSString *test = [_saveDictionary objectForKey:#"Name"];
//This is one of the attempts to set the UITextField
//FIXED by Tomy211(but still does not work)
[titlePage.textName setText:test];
//This is attempting dot notation
titlePage.textName.text = test
titlePage.textName = test //Wrong I know but had to test
//Also made sure test was a proper string
NSLog(test) //Displayed "Dan" as Expected
This is in Journal.m which accessed a reference to the titleVC from a storyboard using this code:
-(UIViewController *)loadView:(NSString *)viewID{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:viewID];
return viewController;
}
The ViewController returned from that method is used in displaying the View in a scrollview, and I havent had an issue with accessing methods from the ViewControllers this way.
All I want to do is let Journal.m set and read the value from textName which is an outlet connected to a UITextField.
-UPDATE-
I checked and discovered I can read the value of the textField but not write to it.
-FINAL UPDATE-
I found the issue with my code. The code was correct(minus the correction by Tomy211), but the issue was where I was calling the code. I was calling the method before the view was displayed, so when the view did get displayed it revert back to the default values.
Note to all that have similar issues:
Check to make sure the view is being displayed before updating a value of a UIObject part of the view. If you have to update values with the view not displayed, use instance variables and have the UIObject get their values from them on the viewDidLoad method.
The problem was I tried to update the TextField objects before the view has been loaded.
Moving the method call to after the point I load the view fixes the problem. I have updated my question to reflect what was correct, and what was incorrect.
Note to all that have similar issues: Check to make sure the view is being displayed before updating a value of a UIObject part of the view. If you have to update values with the view not displayed, use instance variables and have the UIObject get their values from them on the viewDidLoad method.
I have a UITableView which loads through it's navigationController a new viewcontroller.
This code goes in the tableView:didSelectRowAtIndexPath method:
ConcertDetailViewController *detailVC = [[ConcertDetailViewController alloc] initWithNibName:#"ConcertDetailViewController" bundle:nil];
The UITableView has a model, I want to sent an element of this model to the newly created ViewController.
detailVC.aProd = [_prod objectAtIndex:indexPath.row];
When the value is set I want the detailVC to draw the data on the screen. I thought a custom setter, overwriting the one generated by #synthesize would work.
-(void)setaProd:(NSMutableDictionary *)aProd {
_aProd = aProd;
[self displayAProd];
}
displayAProd just takes the values in aProd and put's them on the screen, or rather I'm setting some value of an outlet , created in my nib file.
self.prodNameLbl.text = [_aProd objectForKey:#"name"];
Nothing special about this. But it just doesn't work. I figured out why, I think.
It's because the setter executes way faster then, loading the whole view into memory.
If I put self.prodNameLbl.text = #"something"; in the viewDidLoad method it does display the correct value in the label.
A quick workaround would be the see if _concerts has been set and from there call displayAProd. Here I'm doubting myself if it's a good way to load a view. What if the custom setter takes longer to execute the loading the view. The test to see if _concerts has been set will be false and nothing will be displayed. Or is that just impossible to happen ?
Or maybe there's a better pattern for loading views and passing data to them to be displayed.
Thanks in advanced, Jonas.
The problem is that when you load the view controller from the NIB, the IBOutlets will not be connected to your UILabel and other similar properties during the initWithNibName call.
You need to wait for viewDidLoad to be called on detailVC and call [self displayAProd] from there. At this point, the connections will have been made.
Do a quick test. Put a break point in your didSelectRowAtIndexPath method and, after initialising detailVC, check to see if prodNameLbl is null or not.
Ok I push a detailView when row is selected in the UITableView. In the detailView I set som data and pass that to the webservice. I want to retrieve some data either before the detailView is pushed or when it will disapear. Based on the data retrieved will I set an UIImage as an indicator.
I wonder how I can determine the indexpath.row the detailView got pushed for and use that for setting the UIImage. Is this possible?
I have a UISegmentedControl which loads two different datasources for the UITableView.
For testing I pass the indexpath.row when the detailView is pushed and callback to the UITableView class along with a boolean if the UIImage should be display.
But my problem is that indexpath.row is 0 when the UITableView loads.
And when changing datasource back and forth and passing the indexpath.row with it, the indicator will be display on wrong rows.
The two datasources for the table is:
if (index == 0) {
dictionary = [firstArray objectAtIndex:indexPath.row];
}
else {
dictionary = [secondArray objectAtIndex:indexPath.row];
}
I then set the indicator for that row (will be the UIImage later):
if (indexPath.row == selectedIndex) {
cell.indicator.text = #"Begin";
}
When the detailView is pushed in didSelectRowAtIndexPath i pass in the indexPath.row:
NSInteger rowIndex = indexPath.row;
[self.detailViewController initWithDetailsSelected:rowIndex:dictionary];
When the detailView is done the UITableView class receives the callback:
- (void)processAttendanceSuccessful:(BOOL)successful:(NSInteger)_selectedIndex{
self.selectedIndex = _selectedIndex;
}
So based on a boolean a UIImage should be display for the correct row in the UITableView.
Any suggestions how I can go about this. It must be quite common to set some value in a detailedView and display some data for a specific row in the UITableView, but having a rather hard time finding any good tutorials for this.
I only find how to pass data into the detailView, which I dont have any problem with.
Thanks in advance people.
Don't deselect the row in tableView:didSelectRowAtIndexPath:. When you return to this view controller, just use indexPathForSelectedRow method of the UITableView object.
But in the long run this makes little sense. Since the table view reuses those cells, you also need to remember the rows that have the image. You will probably have to maintain an array for this. In which case, you can either pass the array and the index path to the detail view controller and update them there or use delegate or notification mechanism to update the array from within the table view controller.
I would use an ivar of int, using negative values to represent null selection:
#interface MyController : UIViewController {
int lastSelection;
}
...
#end
#implementation MyController
...
- (void)initWithBlablabla{
...
lastSelection = -1;
}
...
#end
However, I think you should better re-think about your design referring to KVO programming guide. In general MVC patterns, a controller is not the right place to store the state of the model.