Automatically update UITableView when Array changes - iphone

I have a View Controller containing a UITableView that used an NSMutableArray to determine its content via the usual UITableViewDelegate and UITableViewDataSource methods.
In the past I have used NSFetchedResults controller to update the TableView as the result of its query results changing. In this case I would like to update the UITableView appropriately as items are added to/removed from the array.
How should I handle this? Should I wrap the NSMutableArray in a model class with addItem/removeItem methods which use delegation or Notification to trigger reloadData on the UITableView? Or is there a more elegant solution?
There are maximum ~20 items in the array so performance isn't so much of an issue.

Caveat: Non ARC example
You could set up the array which your tableview uses as a property
//.h
..... {
NSArray* array_;
}
#property (nonatomic, retain) NSArray* array;
//.m
Synthesize the array
#synthesize array = array_;
We are interested when array changes so override the setter method
-(void)setArray:(NSArray*)array {
if (array_ != array) {
[array_ release];
array_ = [array retain];
[self.tableview reloadData];
}
}
Then whenever you modify your tableview data, array: At the end of modifying the data.
self.array = newArray;
As it's a property external classes / views will be able to modify array and the table will update.

It seems that NSArrayController is what I want, however it is not available in iOS, so it looks like KVO is my best bet.

Related

Passing an NSMutableArray to a DetailView from UITableViewController - iPhone

I have a UITableViewController which presents another UITableViewController when a cell is tapped. I have an NSMutableArray which I want to pass into the new UITableViewController when instantiated.
I would usually do something like :
- (void)loadStationList {
StationListView * listView = [[StationListView alloc] initWithNibName:#"StationListView" bundle:nil];
listView.dataList = newParser.stationData;
// ...
// Pass the selected object to the new view controller.
NSLog(#"New Parser is %d", [newParser.stationData count]); //This is fine - all objects in array here.
[self.navigationController pushViewController:listView animated:NO];
}
The odd thing is that dataList (the NSMutableArray pointer in the new class) is empty (I am in checking in the number of rows delegate method).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Data List is %d", [dataList count]);
return [dataList count];
}
I have tried several different approaches (such as instantiating a new NSMutable array in the parent class) however nothing seems to work. This may be ARC related as I am still relatively new to ARC. Can anyone help ?
How did you declare dataList,newParser.stationData ?
Should be sth like this
#property (nonatomic, strong) NSMutableArray *dataList;
Anyway, to ensure that you do not loose any values, you may want to copy/assign each element from newParser.stationData to dataList.
Like here:
NSMutableArray * dataList = [[NSMutableArray alloc] init];
for (id arrayElement in newParser.stationData) {
[dataList addObject:arrayElement];
}
Try this instead of the simple assignment listView.dataList = newParser.stationData;. ps don't worry about efficieny this is so fast it will not matter.
Well I got round this by created an instance variable in the app delegate and then saving and reading the array from there. This does look like a bug in arc - it works with other variable types.

NSArray Set property not working

Basically what i am trying to do is make a network refresh and fetch objects, store it in a nsmutable array in my app delegate. Then i have a listviewController which uses that mutable array to display data.
Setting nsarray is not working here is the code:
//Appdelegate code called after pulldown to refresh is done on listview:
[ListView setArrayElements:(NSMutableArray*)sortedArray ];
NSLog(#"sortedArray count:%d",sortedArray);
NSLog(#"ListView Array count:%d",[ListView.ArrayElements count]);
Result i get in log : "sortedArray count:12" (which is perfect)&"ListView Array count:0" (this is not the right result)
It's hard to assume without seeing more of your code but how do you define the ArrayElements property? It may not be retaining itself and you may not have initialized it when the ListView object is created.
Let me know if this works;
Make sure ArrayElements is created in your ListView.h like the following:
#property (nonatomic, retain) NSMutableArray *ArrayElements;
Or on -init or -viewDidLoad of your ListView,
self.ArrayElements = [[NSMutableArray alloc] init];
Don't forget to release what you retained:
- (void)dealloc
{
//.....
[ArrayElements release];
[super dealloc];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.mTableView reloadData];
}
use this method
Do not make the UITableView object as a property. Just use tableView object , remove 'self.' portion.
Tell me if it helps!

NSManagedObjectContext executeFetchRequest returns array containing objects that don't stick around

I have a UITableView I'm populating with data from CoreData. I have a data access class I call a method on to get the array of data to populate the table with. In that method I have something like this:
NSArray *fetchedArray = [context executeFetchRequest:request error:&error];
I was just returning this array directly, but was getting an error in my view controller when I try to set its local property that holds the returned array
#property (nonatomic, retain) NSArray *listData;
and
#synthesize listData; // <-- error here -[CFNumber release]: message sent to deallocated instance...
respectively.
It seemed like the 'retain' in my #property was trying to release the previous listData array, which seemed to have already been released (or, more likely an object in the array or one of its properties had been released).
So in my data access class I added the following after the fetchedArray is produced by the context:
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
for (Response *item in fetchedArray) {
[listArray addObject:item];
}
return listArray;
But I still get the same error in the #synthesize listData back in the view controller. It doesn't happen the first time usually, but after tapping through to the detail controller and then going back to the list and then reloading the list with different data (e.g. filtering based on user input which calls the data access method to return an updated list - hence the error in the setter for listData).
I'm not entirely sure if my problem is memory management related or related to something I'm not understanding about what the context returns. It'd be nice if a fetch request returned data that didn't get released when I think I've retained it. :(
EDIT Note that given the answer, the title of the question may be a bit misleading.
Ah - just had to think it through a bit more. My problem was that I was assigning one of the objects in the array to a property on the detail controller, but calling release on that property in my detail controller's dealloc.
After changing #property (nonatomic, assign) to #property (nonatomic, retain) it doesn't crash. Yay. Sooo looking forward to ARC.

Sharing an array between 2 classes

I currently have two arrays, 1 in each class, but I am cloning them before displaying the other viewController. So whatever happens e.g. delete an item in one viewController, I clone the array for the other ViewController when it needs it.
What is the best way to make these ViewControllers read and write to the same array? I would like a shared array resource which I can access, modify from the 2 viewControllers, possibly a third, whenever necessary.
How is this done without cloning all the time.
If the array is shared only by the two view controllers, just let them point to the same object.
#interface FirstViewController {
//...
NSMutableArray *arrayData;
}
#property (nonatomic, retain) NSMutableArray *arrayData;
#end
#interface SecondViewController {
//...
NSMutableArray *arrayData;
}
#property (nonatomic, retain) NSMutableArray *arrayData;
#end
And somewhere in the code
NSMutableArray *array = [[NSMutableArray alloc] init];
firstViewController.arrayData = array;
secondViewController.arrayData = array;
[array release];
This array conceptually becomes a Model for your design. If the two view controllers perform the same tasks on the data of the array that can be abstracted, consider having a custom class that contains the array and serves as the model class.
You can create a singleton class which holds the array, then access it through the sharedInstance. I recommend this article for a review of the various approaches to this situation.
This is probably a good use of the Model-View-Controller paradigm. Break out the array into the model that both view controllers can access.

Application crashing upon release of UITableViewController

I have a class that retrieves data from core data and stores it into a NSMutablearray. It also has a function that returns this array.
datamanager.h:
#interface DataManager : NSObject {
NSMutableArray *feedItems;
...
}
#property (nonatomic, retain) NSMutableArray *feedItems;
...
datamanager.m:
...
-(void)loadNews{
(load data from core data and put it in self.feedItems)
....
}
-(NSMutableArray*)getAllItems{
return self.feedItems;
}
Now i Have a UIViewController with 2 UIviews (View1 and View2) as IBOutlets. When a button in View1 is clicked it fetches the data from the datamanager class sets it as a NSMutablearray for use for a UItableviewController (tableView1).
After allocating and initializing tableView1 and setting up the Nsmutablearray to populate the table, I add its view as subview to View 2. Now my problem is when I release tableview1 after this procedure I get a EXEC_BAD_ACCESS.
View1 Button IBAction code:
-(void)loadItems{
if ([[dataManager getAllNews] count] > 0) {
ItemTableViewController *tableView1 = [[ItemTableViewController alloc] initWithNibName:#"ItemTableViewController" bundle:nil];
[tableView1 setItemList:[sectionManager getAllItems]];
for (UIView *view in View2.subviews) {
[view removeFromSuperview];
}
[View2 addSubview:tableView1.view];
[tableView1 release]; // if this is not released it works properly else EXEC_BAD_ACCESS
}
}
tableView1 setItemList function:
-(void)setItemList:(NSMutableArray *)list{
self.ItemList = list; //self.ItemList is a NSMutableArray
}
How do I properly release tableView1? Ive tried autorelease too, still doesn't work.
If you are using UIViewController this way, you have to keep your tableView1 retained and then release it later when it is not used anymore. This could be done by placing
[tableView1.view removeFromSuperview];
[tableView1 release];
in your dealloc.
There is a difference between the UITableView and the UITableViewController. The controller should stick around as long as the view is used, so you shouldn't release the UITableViewController (tableView1) there.
Release it in the dealloc method for example.
Also, why do you have a -(NSMutableArray*)getAllItems method? This is an unnecessary step, as a synthesized property already generates getters and setters.
UITableViewControllers are designed to be used with UINavigationControllers and/or UITabBarControllers. They are not meant to be used the way you're doing this.
You CAN make this work, but you're really fighting the OS.
You should either switch to a UINavigationControllers setup, or just use a straight UITableView (without going through a UITableViewController).