Updating NSTableView after adding an item to array - objective-c

I have a tableview whose columns are bound to an array controller. I'm programmatically adding items to the array:
Person is just a custom class;
personsPresent is in another class called Meeting, it's a MutableArray which contains Person objects.
Person *newPerson = [[Person alloc]init];
[[[self meeting] personsPresent] addObject:newPerson];
[[self tableView] reloadData];
This works, but the values don't show up into my table view until I sort the columns. I thought reloadData would do it for me.
In my xib, my nsarraycontroller's objectcotnroller settings is set to Class and Person. It's controller content it's bound to File Owner and it's model key path is meeting.personsPresent.
Any help would be appreciated.
Mark

You're mutating the array behind the array controller's back. Either ask the array controller to -rearrangeObjects (stinky) or use its -addObject: method (better).

Related

How to get [tableView indexPathForSelectedRow] From a different View [duplicate]

This question already has answers here:
What's the best way to communicate between view controllers?
(4 answers)
Closed 8 years ago.
In my TableViewController.m I can easily get the selected row by doing [tableView indexPathForSelectedRow] but I want to get this value from DIFFERENT view. So when my TableViewController goes to my DetailViewController. I want my DetailViewController to have the data of the selected cell.
In a proper architecture, a Controller is designed so that it acts as a black box to other Controllers. In your example, it's not a very good practice that the Details controller searches for the TableViewController's selected item. Instead your TableViewController should use the DetailViewController as a 'service' and somehow pass the selected object as an argument, or set it as a property of the 'service provider' (DetailController).
For example
// this is still the TavleViewController's code
id selectedObject = // ... get selected object somehow... indexPathForSelectedRow or whatever
DetailViewController *newView = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
[newView showDetails:selectedObject];
Here, the function showDetails is just an example, it's a function you implement to pass the seleted object as argument.
EDIT:
How you do it depends on your model. Supose each object in your model represents a person. Your MainTableView has a list of names and the detailView shows the details about the selected Person. In such a case, which is typcial, you would have a class that represents Person, and the list of persons would be somewhere in an array. So...
NSIndexPath *i = [tableView indexPathForSelectedRow];
Person *selectedPerson = [self.myArrayOfPersons objectAtIndex:i.row];
// here you instantiate or show the details view
[detailsView showDetails:selectedPerson]; // this is one option, you could also use a property
// for example
detailsView.selectedPerson = selectedPerson; // this is an alternative to the showDetails,
In the DetailsViewController you could have something like this
- (void)showDetails:(Person *)person
{
// just fill your controls witht the person's information: age, birth place, address...
[self.someTextView setText:person.name]
}

Parameter passing between two views

I use a first view (a class) where there is a button that shows me one second view (another class).
display looks like this:
listContactsViewController viewController * = [[listContactsViewController alloc] init];
UINavigationController * vc = [[UINavigationController alloc] initWithRootViewController: viewController];
[self presentModalViewController: vc animated: YES];
Then in the second view, I select the rows and then I have an "add" button that to display the first view as this:
[self dismissModalViewControllerAnimated: YES];
My problem is that in the second view I have an NSMutableArray that I would like to send to the first view.
If you have an idea.
Thank you.
There are many way to solve this.
Quick: in your second view controller
listContactsViewController
define a delegate property which holds a reference to the presenting controller (the one where you would like to use the NSArray created in listContactsViewController. Then, before dismissing the view controller, call a method in the delegate interface so that your presenting controller can get a copy of the array.
This is just a quick solution to your problem, though, not the best one.
A more correct solution would be to create a "model" object that is accessible from any controller in your app (a singleton would do) that holds the relevant data: listContactsViewController stores the array into the model; the presenting controller gets it from there.
Use Delegates and Protocol.
Refer this tutorial : Passing data between views tutorial – using a protocol & delegate in your iPhone app.

passing data iOS nsmutable array

I have a tableview and a detailview and I can pass data from my tableview to the detail view by including the detail view.h and setting some values from distance with initWithNib.
DetailView *detailView = [[DetailView alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
//passing data gekozenSpel
detailView.gekozenSpel = [tableData objectAtIndex:indexPath.row];
But now I have a data table with multiple entries and one of then is set, that is: can be changed, in the detailview, from 0 to 1 or from 1 to 0, as a string. It works all fine but now I want to apply the change backwards to the table view and that doesn't work.
myTableView *myTableview = [[myTableView alloc] initWithNibName:#"myTableView" bundle:[NSBundle mainBundle]];
[myTableView.theTable replaceObjectAtIndex:location withObject:value];
For if you use an initWithNib way-of-doing then you create a new empty table nsmutablearray. But I want to CHANGE a value in that array at a specific location with a specific content.
Maybe a singleton to access the data from everywhere? I tried but I have to create instances of what I declare as value and then it is private and not public. So I don't understand how you apply a singleton then.
Any help would be appreciated,
Jan
You will need to add a delegate to the DetailView, this delegate will pass a new array to the old view, this array will have the new tableData that you will replece
For more information on the delegate, here are some tutorials
http://www.roostersoftstudios.com/2011/04/12/simple-delegate-tutorial-for-ios-development/
How to use custom delegates in Objective-C
Simple Delegate Example?
I replace in the detail view the property (eigenschap) of the heart symbol (favourite: YES or NO), then that whole array is put over the mother array (dataVoorMijnTabel) to have it updated back into the main table view (mijnTabelView).
[eigenschappendata2 replaceObjectAtIndex:locatiehartje withObject:eigenschaphartje];
mijnTabelView *mtb = [[mijnTabelView alloc] initWithNibName:#"mijnTabelView" bundle:[NSBundle mainBundle]];
mtb.dataVoorMijnTabel = eigenschappendata2;

Binding with the selection of an NSArrayController in another nib

I have two nibs:
Store.nib
Product.nib
Product.nib's File owner is a subclass of NSViewController which has a property product to which various controls are bound:
#property(nonatomic, retain) SRProduct *product;
Store.nib has an NSArrayController object which has been bound to a property of SRApplicationController, which is this property:
#property(nonatomic, retain) NSArray *products;
SRApplicationController has an outlet to that NSArrayController object.
In the -[SRApplicationController init] method I init an SRProductController object with the Product.nib nib. In -[SRApplicationController awakeFromNib] I add the view of the product controller to a view in Store.nib, and I bind the productsArrayController property (the outlet) of the SRApplicationController object to the product of the product controller:
- (id)init {
if (self = [super init]) {
self.productController = [[SRProductController alloc] initWithNibName:#"Product" bundle:nil];
}
return self;
}
- (void)awakeFromNib {
[self.productView removeAllSubviews]; // this method is from a category
[self.productView addSubview:self.productController.view];
[self.productController.view setFrame:self.productView.bounds];
[self.productsArrayController bind:#"selectedObjects" toObject:self.productController withKeyPath:#"product" options:nil];
}
When I run the app, I get no errors, no warnings, the console remains empty, the table view with all products in Store.nib shows all products and I can select them. The problem is that all fields in Product.nib are empty, but they are bound to the product property of the file owner. Can anyone help me with this problem? Thanks in advance. :)
Somewhere there is some sample code that shows how to do this, I can't remember if is Apple code or from somewhere else. Basically what you need to do is have an array controller in each nib file. The array controller in the list style nib should be bound normally and it's array controller should be an accesible property. In the second nib file you need to bind the array controller's content as normal. You also need to make sure that the file's owner of this detail nib has a connection to the file's owner of the list nib. You then bind the sort descriptor for the detail array controller to listController.arrayController.sortDescriptors (it might actually be sortDescriptor can't remember off the top of my head). You also bind the selection index in the same manner. This will allow the array controller in the detail nib to keep up with what is going on in the list nib, after that you just bind each detail element as normal (i.e. the product name text field would have it's value bound arrayController.selection.productName.
If you forget to bind the sort descriptor of the detail nib's array controller to it's counterpart in the list nib the detail nib will update each time the selection changes in the list, but it might not change to the proper product (the binding just passes the selectionIndex not what object is selected).
When allocating the view controller for the Product.nib you should bind its "product" property to your array controller's selection, it can only be done in code, but that will avoid the need for multiple instances of an array controller, and avoid the need to bind them together so they look the same.
Also, I suggest not to bind the array controller's content to your own NSArray, if you do not bind that property the array controller will allocate and manage its own array. You'll be able to add/remove objects from it directly instead of having to rely on your own property to carefully notify the NSArrayController that a change occurred.
The "content" binding is there to allow to bind an array controller's arrangedObjects to the content of another controller to be able to filter and sort the content differently.

Using the same NIB with multiple View Controllers

Basically I want to use a nib file and view controller as a template for a view that I plan to create a number of times. This nib will have a couple of labels and custom views.
The idea is that I will iterate through an array of objects and for each I will create an instance of this controller and set a property to that of the object from the array.
This all works nicely at the moment except for one thing - the labels won't update when I call setStringValue: !!!
I'm using a method within the view controller's code to make the change but it just doesn't work, I'm guessing that the IBOutlet isn't being hooked up properly, which is strange because the custom views are hooking up perfectly.
Any ideas?
Set a breakpoint on awakeFromNib and look in the debugger what the value of the label outlet is. All outlets should have been connected before awakeFromNib is being called. If it is still nil, you have your answer. Calling setStringValue: on nil does exactly "nothing". In that case you have not correctly bound the outlet or maybe you once had it bound correctly and later on changed the name, in that case there should be a yellow warning triangle in Xcode4 or interface builder indicating that something is wrong; however it will not prevent your app from building or running, the outlet will simply keep its initial value after object creation (which is nil).
When you alloc your NSViewControllers, just init with name of NIB:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
Thanks for the replies, they were helpful but not quite what I was getting at.
I ended up solving it by creating an empty NIB and filling it with just a custom NSView and a few other controls. I created an NSView subclass with IBOutlets for those controls and set the custom view's identity to my subclass in interface builder.
The trick in getting it to work each time I wanted to draw it was by making a class method in my subclass that would load the nib and return the view set up the way I wanted.
Code below:
+(id)todoViewFromNibWithFrame:(NSRect)frameRect todoList:(TodoList *)aTodoList
{
NSNib *todoViewNib = [[NSNib alloc] initWithNibNamed:#"TodoView" bundle:nil];
NSArray *objects = nil;
id todoView = nil;
[todoViewNib instantiateNibWithOwner:nil topLevelObjects:&objects];
for (id object in objects) {
if ([object isKindOfClass:[self class]]) {
todoView = object;
[todoView setTodoList:aTodoList];
break;
}
}
[todoViewNib release];
return todoView;
}
Thanks again for the replies!
Steve