NSMutableArray doesn't populate tableView - iphone

I have few table view controllers and I want selections from them to be shown on "ResultTableViewController". I also have array that collects the selected data and when i finally push it to the "ResultViewController" it shows only the last selection. Please help
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
firstarr = [[NSMutableArray alloc]init]; //array with questions
[self.firstarr addObject:[self.data objectAtIndex:indexPath.row]]; //collects the data properly
BodyDetailViewController* vc =[[BodyDetailViewController alloc] initWithNibName:#"BodyDetailViewController" bundle:nil];
vc.someArray = firstarr; //some array - ViewController with Results.
[self.navigationController pushViewController:vc animated:YES];
}

You have to remove this line :
firstarr = [[NSMutableArray alloc]init];
Because you're initializing your NSMutableArray each time you're selecting a cell, so all the data previously stored is erased and vc.someArray is getting an array with only your last selection, that's why ;)
Init your NSMutableArray outside the method, in your viewDidLoad for example

This...
firstarr = [[NSMutableArray alloc]init]; //array with questions
...creates a new array. Whatever was in firstarr previously is gone when you do a new selection.
Either create the array once somewhere else (when the object is initialized?) or append its content to vc.someArray instead of replacing it.

Related

Create a UIView title of a push view using the index of an array

I have a UITableView that pushes to a UIView when the user taps the UITableViewCell. I want to make the title of the push view the text from the cell that the push view is coming from. I am trying to declare an NSUInteger when the row is selected to create a string with objectAtIndex. it is my understanding that objectAtIndex accepts type NSUInteger. This hasn't really been working for me.
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//get index number
NSNumber *selRow = [[NSNumber alloc] initWithInteger:indexPath.row];
NSUInteger rows = (NSUInteger)selRow;
NSLog(#"%#",rows); //this outputs correctly
NSString *pushTitle = [[self.WorkoutListArray objectAtIndex:rows] stringValue]; //error on this line
//declares a new view controller when something is selected -- uses story board to initialize
WorkoutPushViewController *newView = [self.storyboard instantiateViewControllerWithIdentifier:#"WorkoutPush"];
//when selected a new view is pushed
[self.navigationController pushViewController:newView animated:YES];
//adds a title to the push view
newView.title = pushTitle;
}
#end
I am getting an
NSRangeExcpetion error
when I try to declare the string. I've tried a few ways to declare an NSString from the NSArray but haven't had luck yet. I hope my question was clear :)
I forgot that the object I was trying to print had a name and date property. (sorry for making that dumb mistake) The following code does exactly what I wanted it to do.
NSString *pushTitle = [[self.WorkoutListArray objectAtIndex:indexPath.row] workoutName];
//set the title
newView.title = pushTitle;
Thanks for the help and patience.

Tracking UITableViewCells

I have a to-do list app with a bunch of tasks. Each task has a UITableViewCell. After each table view cell is tapped, it creates a view controller with the task at that row's index path's property. These view controllers are all stored in a NSDictionary. This is the code representation of what I just said:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC;
if (![self.detailViewsDictionary.allKeys containsObject:indexPath]){
detailVC = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[self.detailViewsDictionary setObject:detailVC forKey:indexPath];
detailVC.context = self.managedObjectContext;
}else{
detailVC = self.detailViewsDictionary[indexPath];
}
Tasks *task = [[self fetchedResultsController] objectAtIndexPath:indexPath];
detailVC.testTask = task;
[[self navigationController] pushViewController:detailVC animated:YES];
NSLog(#"%#", self.detailViewsDictionary);
}
So this method of creating unique view controllers and storing them with a certain key almost always works. The problem arises when I delete or move the view controllers:
I was under the impression that a cell's gets recycled as you scroll down (dequeue). This means that marking each cell with a number identifier would result in multiple cells for the same identifier.
Also, if you stored each view controller with a indexPath key, how do you make sure the key isn't set to two view controllers..? For example. Let's say you have 4 cells, which means 4 view controllers. You delete cell 3. Cell 4 moves down to cell 3s spot. You create a new cell which goes to spot 4. Now you have two controllers with the same indexPath key! How do you avoid this?? It's screwing up my app right now because tasks that have already been moved are loading their properties in the wrong view controller/cell!
I was suggested this to solve the problem before: "You maintain an NSMutableArray that "shadows" the contents of the table." I was also suggested to use tags. However, I don't understand what how to implement these.
edit: ---random string---
In tasks.m
-(NSString *)uniqueIdentifierString{
static NSString *alphabet = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:20];
for (NSUInteger i = 0U; i < 20; i++) {
u_int32_t r = arc4random() % [alphabet length];
unichar c = [alphabet characterAtIndex:r];
[s appendFormat:#"%C", c];
}
return s;
}
In tableviewcontroller.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC;
Tasks *task = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if (![self.detailViewsDictionary.allKeys containsObject:task.uniqueIdentifierString]){
detailVC = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[self.detailViewsDictionary setObject:detailVC forKey:task.uniqueIdentifierString];
detailVC.context = self.managedObjectContext;
}else{
detailVC = self.detailViewsDictionary[task.uniqueIdentifierString];
}
detailVC.testTask = task;
[[self navigationController] pushViewController:detailVC animated:YES];
NSLog(#"%#", detailVC);
NSLog(#"%#", task.uniqueIdentifierString);
}
One solution would be to decouple your VCs from the table cells. You could store the VCs in your dictionary referenced by an ID that is unique to the task, but not worry about the table cell. If the task is moved or deleted, you can handle that elsewhere without worrying about the VC array being instantly wrong.
Your other option would be to hook into the move/edit methods the logic for iterating over the VC array to adjust the array at that time.
Given that UITableView expects cells to be reused, you can see how it isn't meant to be a storage mechanism for other data.
It would be best to decouple the data so that you aren't bound to the order or existence of them.

Array nil (even when addObject:) [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
addObject: to array not working (array still nil)
EVERYTHING UPDATED
This app is a table view with a tab bar controller. I am logging the count of the array: arrayOfFavourites and even though i add an object is continues to have a nil value, my relating code, all objects shown are allocated and initialized in the code (previous or present) some are instances and some are properties:
ListViewController.m:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"TOUCHED CELL!");
// Push the web view controller onto the navigation stack - this implicitly
// creates the web view controller's view the first time through
[[self navigationController] pushViewController:webViewController animated:YES];
// Grab the selected item
entry = [[channel items] objectAtIndex:[indexPath row]];
if (!entry) {
NSLog(#"!entry");
}
// Construct a URL with the link string of the item
NSURL *url = [NSURL URLWithString:[entry link]];
// Construct a request object with that URL
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Load the request into the web view
[[webViewController webView] loadRequest:req];
// Take the cell we pressed
// IMPORTANT PART
CELL = [tableView cellForRowAtIndexPath:indexPath];
[webViewController setItem:entry];
webViewController = nil;
webViewController = [[WebViewController alloc] init];
[entry release];
}
WebViewController.m:
You shake to favorite a cell
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
cellToPassOn = nil;
NSLog(#"Favouriting"); // YES I KNOW SPELLING
// This is pretty simple, what we do is we take the cell we touched and take its title and link
// then put it inside an array in the Favourites class
Favourites *fav = [[Favourites alloc] init];
ListViewController *list = [[ListViewController alloc] init];
[self setCellToPassOn: [list CELL]];
if (!item) {
NSLog(#"NILLED ITEM");
}
[[fav arrayOfFavourites] addObject:[item autorelease]];
[fav setCell: cellToPassOn];
}
Favourites.m:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"ROWS NO.");
NSLog(#"%i", [arrayOfFavourites count]);
return [arrayOfFavourites count];
}
Favourites:
A CLASS, ON TAB BAR CONTROLLER
WEBVIEWCONROLLER:
CONTROLLER FOR DIFFERENT WEB VIEWS
LISTVIEWCONTROLLER:
DATA PROVIDER
Actually what happens is when i shake i reload the table view data and i add an object to an array (array of favorites) the count is one.... GOOD! But then when i shake again (in a different article, my app has different webViews when i press different cells.) It is still 1... weird.... if i go to favorites class that array remains one.. ok... so as you can see i am returning the arrayOfFavourites count to numberOfRowsInSection (which is 1) but no cells appear and the cellForRowAtIndexPath is never called (using an NSLOG) why is this happening I AM VERY ANNOYED!
In your Favourites.m numberOfRowsInSection function, looks like you should do this:
if(arrayOfFavourites == NULL)
{
arrayOfFavourites = [[NSMutableArray alloc] init];
}
Because you're reinitializing (and likely leaking) in every single call to numberOfRowsInSection (which gets called each time the table needs to know how many rows it must display -- i.e. very often).
You create and destroy a new Favourites object every time you go through -motionBegan:withEvent:, and you create a new array every time you go through -tableView:numberOfRowsInSection:. If you want data to persist beyond those events, you need to keep the objects around.
Let review this part of your code :
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
cellToPassOn = nil;
NSLog(#"Favouriting"); // YES I KNOW SPELLING
// This is pretty simple, what we do is we take the cell we touched and take its title and link
// then put it inside an array in the Favourites class
// HERE you are creating a NEW fav
Favourites *fav = [[Favourites alloc] init];
// HERE you are creating a NEW list
ListViewController *list = [[ListViewController alloc] init];
// SO HERE what is "CELL" doing, returning some constant or static object?
[self setCellToPassOn: [list CELL]];
// HERE what is item and where does it come from
if (!item) {
NSLog(#"NILLED ITEM");
}
// Here you take an array of an object you just created and autoreleasing the item
// this is not the regular way to handle memory management in Cocoa,
// depending on what you are doing to item else where you could get item == deallocated pretty soon
[[fav arrayOfFavourites] addObject:[item autorelease]];
[fav setCell: cellToPassOn];
HERE you are releasing fav
[fav release];
HERE fav don't exist anymore as well as the array to which you've added something to it.
[list release];
item = nil;
}
Unless I'm reading your code incorrectly I have the feeling that you are trying to have some persistence with "volatile" object.
You need to make a #property for those object to survive longer that one method call.
Every time you create a new object it's new and have no knowledge of the precedent one.
And if we look at this code :
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// HERE everytime the tableview is asking for the numberOfRowsInSection, you create a new array
// And that new empty array is replacing your old one.
// That is your "weird" thing, it's doing what you are asking it to do, set it back to a new empty array.
arrayOfFavourites = [[NSMutableArray alloc] init];
NSLog(#"ROWS NO.");
NSLog(#"%i", [arrayOfFavourites count]);
return [arrayOfFavourites count];
}

how to pass a selected array from a uitable to another uitable

Can anybody guide me how to do something like this:
I managed to do the 1st & the last screen, I need to put in another 1 in between them.
Is there any tutorial that i can learn from ?
thanks alot :)
Create a uinavigationcontroller with uitableview should help you achieve your goal here.
There are many tutorials for that. A quick google search landed me this. Basically you can use a single navigation controller and a single uitableviewcontroller class and create add the items to them. If you are unable to grasp the concepts from that tutorial add a comment here on where you are stuck. I will try to edit this post accordingly.
in screen1:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Screen2 *screen2 = [[Screen2 alloc]initWithNibName:.......];
screen2.itsArray = [[NSMutableArray alloc] init];
if(indexPath.row == 1)
{
screen2.itsArray = yourArrayForScreen2_FirstRow;
}
else if(indexPath.row == 2){
screen2.itsArray = yourArrayForScreen2_SecondRow;
}else{ ...... }
[self.viewController pushViewCOntroller .....];
}
in screen2:
NSMutableArray *itsArray; #property and #snynthesize
now use itsArray as you need it. in screen2, do the same as in screen1 for the screen3
What you need is basically an NSMutableArray with NSMutableArray
for eg.
obj1,obj2,obj3...nil is the dict. in your example
NSMutableArray *middleScreenElement1 = [NSMutableArray arrayWithObjects:obj1,obj2,obj3...,nil];
NSMutableArray *middleScreenElement2 = [NSMutableArray arrayWithObjects:obj1,obj2,obj3...,nil];
NSMutableArray *middleScreenElement3 = [NSMutableArray arrayWithObjects:obj1,obj2,obj3...,nil];
NSMutableArray *firstScreen = [NSMutableArray arrayWithObjects:middleScreenElement1,middleScreenElement2,middleScreenElement3];
In your 1st TableView,add the following method..
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableArray *middleScreenArray = (NSMutableArray*)[firstScreen objectAtIndex:indexPath.row];
NextTableViewController *nTVC = [NextTableViewController alloc]initWithArray:middleScreenArray];
}
now you can use the array to populate your table view just like the middle screen.
**note you have to create an initWithArray:(NSMutableArray*)array method in your middleScreenViewController
Hope it helps.. :)

UITableView with NSMutableArray

I’m populating a UITableView based on the values of an NSMutableArray. This table view has search results. If the user clicks in one of the results, one will navigate to another screen. If the user clicks “back”, the search results are filled in again. At this point, while the table view is being repopulated, the old values still appear, just as I want. However, since I’m doing:
- (void)viewWillAppear:(BOOL)animated
{
NSMutableArray *m = [[NSMutableArray alloc] init];
self.searchResultsArray = m;
[m release];
}
The old cells information is no longer available. Thus, the app crashes if the user clicks in one of the old cells or scrolls the UITableView because I’m accessing the mutable array which was reinitialized above.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *cellArray = [searchResultsArray objectAtIndex:indexPath.row];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
appDelegate.selectedCell = [searchResultsArray objectAtIndex:indexPath.row];
}
Do you have any suggestions concerning how should I do this properly?
Thanks.
this is because you are re-initalizing the array each time the view comes back in focus, the array will then have 0 objects in, causing the issue when you select a row and reference an index in the array that simply was wiped when the viewWillAppear is called.
why not init ' self.searchResultsArray ' in viewDidLoad (remembering to undo this with release when the device receives memory warning)
let me know how you get on.
You should reset the array only when the view loads:
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *m = [[[NSMutableArray alloc] init] mutableCopy];
self.searchResultsArray = m;
[m release];
}
You should always call the reloadData: method when changing the data in the UITableViewDataSourceDelegate instance.
Let me know if this helps
Please try below code:
- (void)viewWillAppear:(BOOL)animated
{
// Your search result array with your old values
// Now add or appened new data to your old search results.
[self.searchResultsArray addObject:#"Your Value 1"];
[self.searchResultsArray addObject:#"Your Value 2"];
[self.searchResultsArray addObject:#"Your Value 3"];
[self.tableView reloadData];
// if you need to add new values from a array put this code in a loop.
}
It should helps you...
Thx
It seems to me that you are not initialising the NSArray in the right method (viewDidLoad:).
Using XLData you don't have to care about where you set up the storage, reload the UITableView or add items to the NSArray (searchResultsArray) since it keeps track of the data (NSArray) and updates the UITableView accordingly and on the fly.