Add 2 or more UIViewController class to a Navigation Stack - ios

I have a reusable UIViewController class which has a tableview in it, say Class T. I have a list of things in this to be displayed.
Now when I press on one of the cells I create a new instance (alloc init) of this class and push it to the navigation stack and display new data in the same class T.
The problem comes when I pop the controller it goes to the first instance if the same class but the tableview displays the data which was displayed in the second instance.
I am using an XIB for Class T and not storyboard and segues.
Please help me resolve this issue.
Thanks,
[EDIT - I] Initializing
T *controller = [T alloc] initWithNibName:#"T" bundle:nil];
[self.navigationController pushViewController:controller animation:YES];

Your issue is clearly based on the missing update of the data used in the table view's datasource. I suggest that each controller gets its own object, array or fetched results controller. You can then do something like this:
T *controller = [T alloc] initWithNibName:#"T" bundle:nil];
controller.dataArray = ... // populate the data source
Alternatively, using the same datasource, you have to make sure to check what should be displayed. Maybe you can give your table view controller class a type property that gets checked when displaying the data cell.
if (self.type == HierarchyFirstLevel) {
cell.textLabel.text = ... // populate for first level
}
else if (self.type == HierarchySecondLevel) {
cell.textLabel.text = ... // something else
}
// etc.

Related

Custom object of same type added multiple times inside for loop

I am allocating custom object (viewcontroller in this case) inside for- loop. And everything seems to work fine. But when I tap on the button of first custom object of viewcontroller, the application crashes.
It is because the instance for the custom object is not retained. Although it works fine for the last added object.
Please advice.
dispatch_async(dispatch_get_main_queue(), ^{
NSInteger index = 0;
for (TestStep *obj_Teststep in objTestSuite.testSteps) {
TestStepView * obj_TestStepView = [[TestStepView alloc] initWithNibName:#"TestStepView" bundle:[NSBundle mainBundle]];
obj_TestStepView.testStep = obj_Teststep;
obj_TestStepView.delegate = self;
DMPaletteSectionView *sectionView = [[DMPaletteSectionView alloc] initWithContentView:obj_TestStepView.view andTitle:[NSString stringWithFormat:#"Test Step %# - %#",obj_Teststep.executionOrder,obj_Teststep.apiCallPath] initialState:DMPaletteStateCollapsed withAction:YES andIndex:index];
sectionView.layer.backgroundColor = [NSColor redColor].CGColor;
[sectionArray addObject:sectionView];
index++;
}
[sectionArray addObject:[[DMPaletteSectionView alloc] initWithContentView:self.addNewTestStepView andTitle:#"Add Test Step" initialState:DMPaletteStateExpanded withAction:NO andIndex:0]];
container.sectionViews = sectionArray;
for (int i =0; i<container.sectionViews.count; i++) {
DMPaletteSectionView *dmobj = [container.sectionViews objectAtIndex:i];
dmobj.delegate = self;
}
});
As #trojanfoe says, your design is faulty. You can't create a view controller and add it's view to another view controller without maintaining a strong reference to the view controller.
You create a bunch of TestStepView objects (which I assume are view CONTROLLERS?) Then you pass the view of those objects to a DMPaletteSectionView, but never retain a strong reference to the TestStepView object. That won't work.
When you add a view controller's view to another view controller you should use the parent/child view controller support that was added to iOS (in iOS 5, if I remember correctly.) Do a search in the Xcode docs in the UIViewController class reference for the words "parent" and "child". There are a family of methods that let you set that up.
You would need to make your TestStepView (view controller?) a child of the DMPaletteSectionView (view controller?)
BTW, stop calling view controllers views, both in your question and in your code. View objects and view controller objects are totally different, and you will confuse both yourself and your readers by calling view controllers views.
I use the abbreviation VC for view controllers in my code to keep my class names shorter, while keeping it clear that they are view controllers, not views.
You are allocating the view controllers and then effectively throwing them away as ARC will deallocate them when they go out-of-scope:
for (TestStep *obj_Teststep in objTestSuite.testSteps) {
TestStepView * obj_TestStepView = [[TestStepView alloc] initWithNibName:#"TestStepView"
bundle:[NSBundle mainBundle]];
// ...
// ARC will deallocate obj_TestStepView here
}
This isn't how you are supposed to use view controllers; they are expected to be presented (normally one-at-a-time), so what you are doing is undefined.

Delegation: delegated method doesn't see outlets

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.

IOS MVC pattern and data controller for manipulating NSMutableArray

I have:
a class called Care
an NSMutableArray that is called masterCareList
masterView, detailView
careDataController for adding Cares to List and count number of objects.
I want to count my objects in MasterView and I want to add an object in detailView. In both cases using the careDataController (not rewind segue or passing data between views) and to the same masterCareList.
How do I use the same dataController and therefore the same NSMutable array in the best (MVC) way?
You could create your careDataController on initial setup of your app and access the careDataController through a property of MasterView. This would enable you to encapsulate counting and other operations on your data separate from your view controller.
a very high level example somewhere where you create MasterView...AppDelegate maybe...
//Appdelegate.m file
MasterView *mv = [[MasterView alloc] init];
DataController *careDataController = [[DataController alloc] init];
mc.model = careDataController; //model is a property within MasterView that you create
[self presentViewController:mv animated:YES completion:nil];
now you could essentially in MasterView present the DetailView after creating it and setting its model property you create to MasterViews model property you set above.
//some procedure in MasterView.m
DetailView *dv = [[DetailView alloc] init];
dv.model = self.model; //self.model you set above
[self presentViewController:dv animated:YES completion:nil];
now once in the DetailView you can add care objects to the original careDataController you initially created at the start of your app...
//somewhere in DetailView.m
Care *careObject = [[Care alloc] init];
[self.model addObject:careObject];
when you dismiss the DetailView after you are done adding an object your careDataController (model property) in MasterView will now have the new care object you added in the DetailView.
EDIT:
I see your new comment, in a way its essentially what I just wrote above which I do not feel is incorrect. Another thing you could do is instead of passing it like above, have MasterView be a delegate for DetailView. When you add a Care object save that object state in DetailView and pass it back using a the protocol you defined in DetailView. You might have a method in the protocol like below that MasterView implements...
-(void)detailView:(DetailView *)dv didInsert:(Care*)careObject
{
[self.model addObject:careObject];
}

ios - Populating a new view controller with data

When creating a new view controller to be pushed onto the stack, what is the correct method to use to populate that view controller with data?
I have a data object that I need to send to the view controller, which will then set it's text fields, etc. with the data.
ItemDetailViewController_iPad *detailViewController = [[ItemDetailViewController_iPad alloc] initWithNibName:#"ItemDetailViewController_iPad" bundle:nil];
[detailViewController populateWithData:_data];
[self.navigationController pushViewController:detailViewController animated:YES];
in ItemDetailViewController_iPad:
-(void) populateWithData:(Item*)_data
{
self.data = _data;
self.navigationItem.title = self.data.title;
self.descriptionText.text = self.data.desc; //the text does not get updated - it's the default text from the nib file
NSLog(#"Desc: %#", self.data.desc); //this logs valid data
}
You can also declare a property in that VC and then set that property before pushing it. Then in the viewDidLoad method for the pushed VC, set the view's title and text field.
The code you've posted should work fine.
There are lots of ways to do this, and it often comes down to what you understand best, feel most comfortable with, and isn't too dull to type in.
Here are some other options.

Cannot pass messages between main view controller and popover view

I'm seem unable to get any kind of communication going between my Main View Controller and a Table View Controller which is being displayed inside a Popover View (iPad).
I'm setting up the Table View inside a Navigation Controller in the usual way:
// create popover
if (self.popoverController == nil) {
filesViewController = [[[MyTableViewController alloc] initWithFiles:fileList] autorelease];
UINavigationController *navCtrl = [[[UINavigationController alloc] initWithRootViewController:filesViewController] autorelease];
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:navCtrl];
self.popoverController.delegate = self;
// resize popover
self.popoverController.popoverContentSize = CGSizeMake(320.0, 44 + [fileList count] * 44);
}
Everything is working fine, and I'm passing an array of file names (fileList) into the Table View, which is held in the Table View as an array called listOfFiles. The Table View displays the filenames, and when one is selected by the user I want to pass that filename back to the Main View Controller. However, I cannot get any communication going back from the Table View's didSelectRowAtIndexPath method to the Main VC. I've tried all sorts of outlets going in various directions, and I've tried creating a new object in didSelectRowAtIndexPath to handle the filename coming from the Table View. I can pass the filename out to the new object, but when I try to send that into the Main VC it is null again. Everything I send to my Main VC while that popover is active comes up as null.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
NSLog(#"%#", handler.addressForImageFile);
self.popoverController = nil;
[self.popoverController release];
}
Is there some reason why my Main VC won't get anything but null objects from my Table View? I've spent days trying so many different things. I feel like there's some fundamental gap in my knowledge of how popovers work. Surely there is a simple way to send a string back to my Main VC when it is selected from the Table View?
Thanks so much for any help!
There's propably a much better way to do this, but depending on the goal of passing the string, one way could be to use NSUserDefaults.

Resources