addObject: to array not working (array still nil) - objective-c

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];
[fav release];
[list release];
item = nil;
}
Favourites.m:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
arrayOfFavourites = [[NSMutableArray alloc] init];
NSLog(#"ROWS NO.");
NSLog(#"%i", [arrayOfFavourites count]);
return [arrayOfFavourites count];
}

Why are you inializing the array in tableview:numberOfRowsInSection ? This will cause the array to be reset each time table view is reloaded. This could be your issue.

you are allocating your array in -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
try to allocate it somewhere else.

You could allocate the arrayOfFavorites in the tableView:numberOfRowsInSectionMethod, but then you first need to check if it is nil.
if( !arrayOfFavorites )
arrayOfFavoriges = [[NSMutableArray alloc] init];
You should release it then in the dealloc method: [arrayOfFavorites release].

-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
cellToPassOn = nil;
NSLog(#"Favouriting"); // YES I KNOW SPELLING
// HERE creation of a Brand NEW empty Favourites instance
Favourites *fav = [[Favourites alloc] init];
// HERE creation of a Brand NEW empty ListViewController instance
ListViewController *list = [[ListViewController alloc] init];
// HERE we hope that the ListViewController as CELL other then nil when it is Brand NEW
[self setCellToPassOn: [list CELL]];
if (!item) {
NSLog(#"NILLED ITEM");
}
[[fav arrayOfFavourites] addObject:[item autorelease]];
[fav setCell: cellToPassOn];
[fav release];
// HERE the fav instance get deallocated and don't exist anymore
[list release];
// HERE the list instance get deallocated and don't exist anymore
item = nil;
}
In this code list and fav exist only in the body of this method, attempt to get to the value they have hold done to will failed, because list and fav doesn't exist outside that method.

Related

I'm trying to implement infinite scrolling into my UITableViewController and unsure how to use the database results returned

Currently trying to implement infinite scrolling into my app using this plugin: https://github.com/pronebird/UIScrollView-InfiniteScroll
So far I've added this code to my tableview controller viewDidAppear and viewDidDisappear methods:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// setup infinite scroll
// keep a weak reference to table view
__weak UITableView *weakTableView = self.tableView;
[self.tableView addInfiniteScrollWithHandler:^{
// keep a strong reference to table view
__strong UITableView *strongTableView = weakTableView;
// seems like our table view didn't make it
if(strongTableView == nil) return;
//
// fetch your data here, can be async operation,
// just make sure to call finishInfiniteScroll in the end
// finish infinite scroll animation
[strongTableView finishInfiniteScroll];
}];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// remove infinite scroll
[self.tableView removeInfiniteScroll];
[[self tableView] reloadData];
}
I drag the table and the spinner shows underneath the last row and disappears after a second or two. Now all I need to do is get the data from my array and add it to block in the viewDidAppear code.
This is how I currently get my parse.com data into an NSMuteableArray instance named "people":
- (void)populatePeopleArrayWithCloudData {
// Grab data for datasource and store in people array
NSLog(#"view did load");
people = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:#"People"];
[query whereKey:#"active" equalTo:#1];
[query orderByDescending:#"createdAt"];
[query setLimit:10];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
Person *person = [[Person alloc] init];
[person setName:[object objectForKey:#"name"]];
[person setNotes:[object objectForKey:#"notes"]];
[person setAge:[[object objectForKey:#"age"] intValue]];
[person setSince:[object objectForKey:#"since"]];
[person setFrom:[object objectForKey:#"from"]];
[person setReferenceNumber:[object objectForKey:#"referenceNumber"]];
PFFile *userImageFile = object[#"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData];
[person setImage:image];
}
}];
[person setActive:[[object objectForKey:#"active"] intValue]];
[person setObjectId:[object objectId]];
[people addObject:person];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
NSLog(#"Calling reloadData on %# in viewDidLoad", self.tableView);
[self.tableView reloadData];
}];
}
I limit results to 10. Now what I wish to do is keep grabbing the next 10 results that haven't already been grabbed every time I scroll to the bottom of the table. This code that helps me do this needs to go in the block mentioned above.
The "people" instance is used by my tableviewdatasource methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [[self tableView] dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Person *current;
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
current = [searchResults objectAtIndex:indexPath.row];
} else {
current = [people objectAtIndex:[indexPath row]];
}
[[cell textLabel] setText: [current name]];
[[cell imageView] setImage: [current image]];
[[cell detailTextLabel] setText: [current notes]];
return cell;
}
How do I use my database results with this plugin? As you can see I limit results to 10 and I need to grab the next 10 when I've scrolled to the bottom of the table and add them after the last row in the table.
Kind regards
UPDATE - my numbers of rows in section method as it stands:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
return [searchResults count];
} else {
return [people count];
}
}
Happy to help, but you should probably give it a shot first for us to provide feedback. A few thoughts to get you going...
The general idea is to use the "skip" property on PFQuery to get the next 10. Each time you call it, you add 10.
So create your query as you do, keep it around in a property, but move your findObjectsInBackgroundWithBlock call to your infiniteScrollHandler, adding 10 to skip each time after you call it. Then at the end of the handling (where you call table reload now), call [strongTableView finishInfiniteScroll]
In your numberOfRows, you'll have to provide the maximum number of people available at your source.

ViewController load methods called when view disappears?

I've got the following code in a viewController. I've got a NavigationController on the view (which is the child view - the code for the parent is working fine)
What happens is when I select an option on the parent, this viewController loads. The user can select an option from the child viewController to open a PDF file with a DocumentInteractionController (which works fine).
The problem is when I try going back to the parent viewController, messages are being sent to the child viewController as if it's still allocated. I saw something similar when I set it up since there were multiple calls to the methods in the child viewController.
Any thoughts on what I'm doing wrong?
#import "DetailViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
#synthesize node;
#synthesize replies;
#synthesize docController;
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
[self.tableView setContentOffset:CGPointZero animated:NO];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.docController init];
// Do any additional setup after loading the view from its nib.
}
- (void) dealloc
{
[self.docController release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.replies == nil)
{
self.replies = [[NSArray alloc] init];
self.actions = [[NSArray alloc] init];
}
if(self.replies.count == 0)
{
self.replies = [self.node nodesForXPath:#"./question/reply/text" error:nil];
self.actions = [self.node nodesForXPath:#"./question/reply/response/action" error:nil];
}
return self.replies.count;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"QuestionCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Get the object to display and set the value in the cell
NSString *cellText = [[replies objectAtIndex:indexPath.row] stringValue];
cell.textLabel.text = cellText;
return cell;
}
- (void) showOptionsMenu:(NSString *) fileName
{
NSString *fileToOpen = [[NSBundle mainBundle] pathForResource:fileName ofType:#"pdf"];
NSURL *fileURL = [NSURL fileURLWithPath:fileToOpen];
self.docController = [self setupControllerWithURL:fileURL usingDelegate:self];
bool didShow = [self.docController presentOptionsMenuFromRect:CGRectMake(0, 0, 150, 150) inView: self.view animated:YES];
if(!didShow)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Sorry, app not found" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *action = [[self.actions objectAtIndex:indexPath.row] stringValue];
[self showOptionsMenu:action];
}
- (UIDocumentInteractionController *) setupControllerWithURL: (NSURL *) fileURL usingDelegate:(id <UIDocumentInteractionControllerDelegate>) interactionDelegate
{
UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
interactionController.delegate = interactionDelegate;
return interactionController;
}
#end
EDIT
Adding the code for the parent view controller...maybe there's something I'm doing wrong in there? I'm using GDataXML to load a Q&A app based on the contents of an XML file...
#implementation ViewController
#synthesize currentReply;
#synthesize questions;
- (void)viewDidLoad
{
[super viewDidLoad];
[self setUpQuestions];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc
{
[super dealloc];
}
- (void) setUpQuestions
{
// create and init NSXMLParser object
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"query" ofType:#"xml"];
NSData *xml_data = [[NSData alloc] initWithContentsOfFile:filePath];
NSError *error;
GDataXMLDocument *xmlDoc = [[GDataXMLDocument alloc] initWithData:xml_data options:0 error:&error];
NSArray *rootDataArray = [xmlDoc.rootElement nodesForXPath:#"//query" error:nil];
for (GDataXMLElement *rootDataElement in rootDataArray)
{
// Allocate the query object
self->query = [[[Query alloc] init] autorelease];
// Name
NSArray *query_title = [rootDataElement elementsForName:#"text"];
if (query_title.count > 0)
{
GDataXMLElement *queryTitle = (GDataXMLElement *) [query_title objectAtIndex:0];
self->query.queryTitle = [[[NSString alloc] initWithString:queryTitle.stringValue] autorelease];
}
NSArray *query_first_question = [rootDataElement elementsForName:#"question"];
NSArray *replies = [NSArray alloc];
questions = [[NSMutableArray alloc] init];
if(query_first_question.count == 1)
{
GDataXMLElement *fq = (GDataXMLElement *) [query_first_question objectAtIndex:0];
replies = [fq elementsForName:#"reply"];
for (GDataXMLElement *replyElement in replies)
{
[questions addObject:replyElement];
}
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Only one section.
return 1;
}
- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
switch(section)
{
case 0:
return questions.count;
break;
case 1:
return 1;
break;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"QuestionCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
// Get the object to display and set the value in the cell.
GDataXMLElement *questionAtIndex = questions[indexPath.row];
NSString *cellText = [[[questionAtIndex elementsForName:#"text"] objectAtIndex:0] stringValue];
cell.textLabel.text = cellText;
//cell.textLabel.text = [[questionAtIndex elementsForName:#"text"] objectAtIndex:0];
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSMutableString *msg = [NSMutableString new];
//[msg appendString:#"You selected row: "];
//[msg appendString:[NSString stringWithFormat:#"%i",indexPath.row]];
//UIAlertView *alertMsg = [[UIAlertView alloc] initWithTitle:#"Row Selected" message:msg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
//[alertMsg show];
if (questions != nil)
{
GDataXMLElement *selectedReply = (GDataXMLElement *) [questions objectAtIndex:indexPath.row];
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
dvc.node = selectedReply;
[self.navigationController pushViewController:dvc animated:YES];
[dvc release];
}
}
EDIT
I've tried profiling and looking for zombies, but when the crash occurs there are no zombie objects flagged. It throws the following error in the console:
[UIView _forgetDependentConstraint:]: message sent to deallocated instance 0x1e8ab810
I have seen this Issue before also !!!
Answer : Turn Off "AutoLayout".
I am guessing the error occurred due to new feature in ios called AutoLayout. It looks like Compiler has created some NSLayoutConstraint objects and due to some reason the objects were released more than they should. Deletion and Re-Creation, forces Xcode to Re-Build the Constraints. But,I am not 100% sure.
Try to Un-Check "AutoLayout", if it can solve your Problem.
Your DetailViewController code is fine - not actually fine, as you're leaking self.replies and self.actions, and the [self.docController init] is very odd and probably wrong (always alloc and init together) - but the lifecycle code on this end looks fine. The problem is almost certainly in the parent view controller (or possibly the document controller if you're creating a retain cycle there). If the parent view controller is holding onto a pointer to the detail view controller, it won't actually be deallocated and accessing the view or any property thereof will cause -viewDidLoad to be called again.
From what I understood, your parent view controller is setting the node here:
dvc.node = selectedReply;
and it's never being released from your DetailViewController.
I'm assuming that your GDataXMLElement in the DetailViewController header is set as "retain".
And there's some leaking problems as icodestuff pointed out.

NSMutableArray to NSString and Passing NSString to Another View IOS5.1

I have an NSMutableArray of names. I want the pass the data (selected name) inside of NSMutableArray as text to another view's label.
FriendsController.m:
- (void)viewDidLoad {
[super viewDidLoad];
arrayOfNames=[[NSMutableArray alloc] init];
arrayOfIDs=[[NSMutableArray alloc] init];
userName=[[NSString alloc] init];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
long long fbid = [[arrayOfIDs objectAtIndex:indexPath.row]longLongValue];
NSString *user=[NSString stringWithFormat:#"%llu/picture",fbid];
[facebook requestWithGraphPath:user andDelegate:self];
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]];
FriendDetail *profileDetailName = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
profileDetailName.nameString=userName;
[profileDetailName release];
}
- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSData class]]) {
transferImage = [[UIImage alloc] initWithData: result];
FriendDetail *profileDetailPicture = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
[profileDetailPicture view];
profileDetailPicture.profileImage.image= transferImage;
profileDetailPicture.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:profileDetailPicture animated:YES];
[profileDetailPicture release];
}
}
In FriendDetail.h
NSString nameString;
IBOutlet UILabel *profileName;
#property (nonatomic, retain) UILabel *profileName;
#property (nonatomic, retain) NSString *nameString;
In FriendDetail.m
- (void)viewDidLoad
{
[super viewDidLoad];
profileName.text=nameString;
}
nameString in second controller(FriendDetail) returns nil. When i set a breakpoint in firstcontroller I see the string inside of nameString is correct but after that it returns to nil somehow.
-----------------------EDIT----------------------------------------
According to answers I have improved my code little bit
FriendsController.h
FriendDetail *friendController;
#property (strong, nonatomic) FriendDetail *friendController;
FriendsController.m
- (void)viewDidLoad
{
[super viewDidLoad];
arrayOfNames=[[NSMutableArray alloc] init];
arrayOfIDs=[[NSMutableArray alloc] init];
arrayOfThumbnails=[[NSMutableArray alloc] init];
userName=[[NSString alloc] init];
friendController= [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
}
-(void)request:(FBRequest *)request didLoad:(id)result{
if ([result isKindOfClass:[NSData class]])
{
transferImage = [[UIImage alloc] initWithData: result];
friendController.nameString=userName;
[friendController view];
friendController.profileImage.image= transferImage;
friendController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:friendController animated:YES];
}
//this is how i take facebook friends list
if ([result isKindOfClass:[NSDictionary class]]){
items = [[(NSDictionary *)result objectForKey:#"data"]retain];
for (int i=0; i<[items count]; i++) {
NSDictionary *friend = [items objectAtIndex:i];
long long fbid = [[friend objectForKey:#"id"]longLongValue];
NSString *name = [friend objectForKey:#"name"];
NSLog(#"id: %lld - Name: %#", fbid, name);
[arrayOfNames addObject:[NSString stringWithFormat:#"%#", name]];
[arrayOfIDs addObject:[NSNumber numberWithLongLong:fbid]];
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
long long fbid = [[arrayOfIDs objectAtIndex:indexPath.row]longLongValue];
NSString *user=[NSString stringWithFormat:#"%llu/picture",fbid];
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]];
[facebook requestWithGraphPath:user andDelegate:self];
[username retain]
}
Now when i select row first time it sends name. When i come back to tableview and select another name it shows the old name.
If I delete [username retain] in didSelectRowAtIndexPath: it still sends nil to nameString
when I set break point at didSelectRowAtIndexPath: at line `
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]]`
I can see userName = #"Adam Dart" which is correct
in my second breakpoint at line friendController.nameString=userName; I see that nameString =nil and userName = Variable is not CFString
ARC is set to NO
The value is nil because you did not pass the value in request:didLoad: function.
In function didSelectRowAtIndexPath, You create a local instance of another ViewController and set the value of nameString, but you did not present the view and release the ViewController immediately. You actually do nothing in these few lines of code:
FriendDetail *profileDetailName = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
profileDetailName.nameString = userName;
[profileDetailName release];
In function request:didLoad:, again you create a local instance of another ViewController with image. But this instance is only local to this function, which means no relation to the one created in didSelectRowAtIndexPath.
What you need to do is, remember the name of clicked row first in didSelectRowAtIndexPath, here you dont have to create the ViewController instance. When the request finish, set both the image and name to the controller and then present it. But you should avoid user from clicking different rows at the same time, because you don't know when the request finish.
You have two instances of FriendDetail called profileDetailPicture. Both of theses profileDetailPicture are not the same. So in your didSelectRowAtIndexPath method, the value that you assigned to the nameString will not be visible/available to the nameString of the profileDetailPicture In the request:(FBRequest *)request didLoad method.
Edit for solution:
Create an iVar or property (profileDetailPicture) in the FriendController.
Only do one allocation in the request:(...) method.
Remove the allocation statement in the didSelectRowAtIndexPath.
Any chance it has to do with the fact that you assign to profileDetailName and then immediately release it?
profileDetailName.nameString=userName;
[profileDetailName release];
You have to allocate the "first_controller" in your "second_controller"
to pass objects such as your string. and you would call the nameString differently.
example:
second_controller.h
#import "first_controller.h"
...
#interface second_controller : UIViewController{
first_controller* firstController;
}
second_controller.m
- (void)viewDidLoad {
[super viewDidLoad];
firstController = [[first_controller alloc] init];
profileName.text = firstController.nameString;
}
Which you'll have to init it correctly, because its two views sharing information.

UITableView Data Source issue. My data array is suddenly empty when UITableView:cellForRowAtIndexPath: is called

I am working through The BNR Guide to iOS programming, and having some trouble trying to solve a challenge to divide up UITableView data into sections. The summary of what I have going on is this. I needed a flexible way to manage the sections of the tableView, so I have built an NSMutableArray to hold the sections in. Each object in the array represents a table section via NSDictionary. The dictionary has 2 keys, a string for the section header and an array to hold the possessions of the section. I have a little routine that gets allPossesions from the store class and sorts and stores them into the appropriate arrays and dictionary. I've been working through rewriting my code to incorporate this and I have hit a confusing road block. As my app runs in the debugger an am throwing out a lot of NSLogs to track whats going on. I don't seem to have any trouble accessing and logging the contents of my sections array or it's nsdictionaries in of most of the support methods for the table view; but when cellForRowAtIndexpath is called, the code gods stop smiling on me. Somehow when I try to access or log out my possessions array it is suddenly empty.
I cannot for the life of me figure this out. I have been beating my head at this for a little over a day now and would gladly accept any input or help. Below is my itemsViewController.h and implementation. Please overlook the mess of logs and commented out sections. I've been trying to figure this out and left them in so people could possible tell me what I may need to change about my approach to this. Also it may be worthy to note that the table is initially empty and the app doesn't have any trouble till I try to add something to it.
//
// ItemsViewController.h
// HomePwnr
#import <Foundation/Foundation.h>
#import "ItemDetailViewController.h"
#interface ItemsViewController : UITableViewController
{
NSMutableArray *sections; // variable to hold the number of sections
}
-(void)addNewPossession:(id)sender;
-(void)divideSections;
#end
Here is the implementation
//
// ItemsViewController.m
// HomePwnr
#import "ItemsViewController.h"
#import "PossessionStore.h"
#import "Possession.h"
#implementation ItemsViewController
- (id) init
{
NSLog(#"ItemsViewController init called");
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// create a new barItem that will send addNePossession: to itemsViewController
UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNewPossession:)];
// Set this barButtonItem as the right item in the Navigation item
[[self navigationItem] setRightBarButtonItem:bbi];
//The Navigation item retains its buttons so release bbi
[bbi release];
// set the title of the navigation item
[[self navigationItem] setTitle:#"Homepwner"];
//[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
}
sections = [[[NSMutableArray alloc] init] retain]; // added the extra retain here to make sure my sections weren't getting released pre maturely
// set up sections here by dividing allPossessions
[self divideSections];
return self;
}
- (id) initWithStyle:(UITableViewStyle)style
{
return [self init];
}
-(void)divideSections
{
NSLog(#"divideSections called");
// For simplicity we'll just empty out the sections array and rebuild it each time we add or modify a possesion
[sections removeAllObjects];
NSArray *cheapStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:#"valueInDollars < 50"]];
NSArray *expensiveStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:#"valueInDollars >= 50"]];
// we'll be making an NSDictionary for each section. it will hold an array of possesions for each section and it's key will serve as the sections header
if ([cheapStuff count] > 0) {
NSMutableDictionary *section1 = [NSMutableDictionary dictionaryWithObject:cheapStuff forKey:#"Possessions"];
[section1 setValue:#"Cheap Stuff" forKey:#"Header"];
[sections addObject:section1];
// sections now retains NSDictionary so we release it
[section1 release];
}
if ([expensiveStuff count] > 0) {
NSMutableDictionary *section2 = [NSMutableDictionary dictionaryWithObject:expensiveStuff forKey:#"Possessions"];
[section2 setValue:#"Cheap Stuff" forKey:#"Header"];
[sections addObject:section2];
// sections now retains NSDictionary so we release it
[section2 release];
}
//now our arrays are retained by the dictionarys so we release them
[cheapStuff release];
[expensiveStuff release];
NSLog(#" End of divideSections sections holding %#", sections);
}
/*
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ItemDetailViewController *detailViewController = [[[ItemDetailViewController alloc] init] autorelease];
// NSArray *possessions = [[PossessionStore defaultStore] allPossessions];
// give the detail view controller a pointer to the possesion object in row
// get the NSDictionary located at the section index, get the dictionary's array, get the possession at row index
Possession *p = [[[sections objectAtIndex:[indexPath section]] objectAtIndex:0] objectAtIndex:[indexPath row]];
[detailViewController setPossession:p];
// push it onto the navigationControllers stack
[[self navigationController] pushViewController:detailViewController animated:YES];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
////If the table view is asking to commit the delete command
if (editingStyle == UITableViewCellEditingStyleDelete) {
//PossessionStore *ps = [PossessionStore defaultStore];
// NSArray *possessions = [ps allPossessions];
// Possession *p = [possessions objectAtIndex:[indexPath row]];
int section = [indexPath section];
int row = [indexPath row];
Possession *p = [[[sections objectAtIndex:section] objectAtIndex:0] objectAtIndex:row];
[[[PossessionStore defaultStore] allPossessions] removePossession:p];
// remove the row from the table view with an animation
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
-(void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
[[PossessionStore defaultStore] movePossessionAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]];
}
*/
-(void)addNewPossession:(id)sender
{
//
NSLog(#"addNewPossession called - sections = %#", sections);
[[PossessionStore defaultStore] createPossession];
//NSLog(#"Possesion store now holds %#", [[PossessionStore defaultStore] allPossessions]);
//we've added a new possession so we'll divide out the sections again
[self divideSections];
//NSLog(#"addNewPossession exiting - sections = %#", sections);
//tableview returns the tablesview
[[self tableView] reloadData];
//NSLog(#"table view reloading data - sections = %#", sections);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int numSections = [[(NSArray *)[sections objectAtIndex:section] objectForKey:#"Possessions"] count];
//NSLog(#"numberOfRowsInSection: called for section %i, returning %i.", section, numSections);
return numSections;
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"returning number of sections: %i", [sections count]);
// return the count of the sections array
return [sections count];
}
- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSLog(#"tableView:titleForHeaderInSectionCalled - sections = %#", sections);
//Configure the header titles based on the number of sections
if ([sections count] <= 1) {
// return simple title for only one section in table
NSLog(#"Returning My Stuff");
return #"My Stuff";
} else {
NSLog(#"The header returned is %#", [[sections objectAtIndex:section] objectForKey:#"Header"]);
// or return the key for the dictionary entry for the current section
return [[[sections objectAtIndex:section] objectAtIndex:section] objectForKey:#"Header"];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"tableView:cellForRowAtIndexPath called for section %d, Row %d", [indexPath section], [indexPath row]);
NSLog(#"Sections = %#", sections);
NSLog(#"The Dictionary is %#", [sections objectAtIndex:[indexPath section]]);
//NSLog(#"thisSection array should be %#", (NSArray *)[[sections objectAtIndex:thisSection] objectForKey:#"Possessions"]);
//NSArray *thisSectionArray = [[sections objectAtIndex:thisSection] objectForKey:#"Possessions"];
// Check for reusable cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
// If there is no cell of this type create a new one
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"] autorelease];
}
// get the NSDictionary located at the section index, get the dictionary's array, get the possession at row index
//Possession *p = [thisSection objectAtIndex:[indexPath row]];
//[[cell textLabel] setText:[p description]];
[[cell textLabel] setText:#"cell text"];
return cell;
}
-(void)viewWillAppear:(BOOL)animated
{
[self divideSections];
[super viewWillAppear:YES];
NSLog(#"viewWillAppear called - sections = %#", sections);
}
-(void)viewDidUnload
{
[super viewDidUnload];
NSLog(#"viewDidUnload called - sections = %#", sections);
}
#end
Lastly here is my log from trying to run the app. The little green indicator is sitting right on the line where I try to log the contents of sections during cellForRow....... after the app crashes.
2012-03-10 06:22:00.177 HomePwnr[44399:f803] ItemsViewController init called
2012-03-10 06:22:00.180 HomePwnr[44399:f803] divideSections called
2012-03-10 06:22:00.181 HomePwnr[44399:f803] End of divideSections sections holding (
)
2012-03-10 06:22:00.188 HomePwnr[44399:f803] divideSections called
2012-03-10 06:22:00.189 HomePwnr[44399:f803] End of divideSections sections holding (
)
2012-03-10 06:22:00.189 HomePwnr[44399:f803] returning number of sections: 0
2012-03-10 06:22:00.190 HomePwnr[44399:f803] returning number of sections: 0
2012-03-10 06:22:00.191 HomePwnr[44399:f803] viewWillAppear called - sections = (
)
2012-03-10 06:22:04.234 HomePwnr[44399:f803] addNewPossession called - sections = (
)
2012-03-10 06:22:04.235 HomePwnr[44399:f803] divideSections called
2012-03-10 06:22:04.237 HomePwnr[44399:f803] End of divideSections sections holding (
{
Header = "Cheap Stuff";
Possessions = (
"Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000"
);
}
)
2012-03-10 06:22:04.238 HomePwnr[44399:f803] returning number of sections: 1
2012-03-10 06:22:04.239 HomePwnr[44399:f803] tableView:titleForHeaderInSectionCalled - sections = (
{
Header = "Cheap Stuff";
Possessions = (
"Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000"
);
}
)
2012-03-10 06:22:04.240 HomePwnr[44399:f803] Returning My Stuff
2012-03-10 06:22:04.241 HomePwnr[44399:f803] tableView:titleForHeaderInSectionCalled - sections = (
{
Header = "Cheap Stuff";
Possessions = (
"Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000"
);
}
)
2012-03-10 06:22:04.241 HomePwnr[44399:f803] Returning My Stuff
2012-03-10 06:22:04.243 HomePwnr[44399:f803] tableView:cellForRowAtIndexPath called for section 0, Row 0
(lldb)
Well, two suggestions. The first is to move to ARC (Automatic Reference Counts) so the compiler will take care of all your retain/release etc.
Short of that, your immediate problem is in "divideSections" where you release section1 and section2. Both are created with the convenience routine dictionaryWithObject, which returns an auto-released dictionary. Thus even though it is retained by the alloc routine and by adding it to the array sections, it has a retain count of zero and so can disappear at any time. (You can know that this is the case because of the convention that only routines with "alloc, copy or retain" will returned retained objects)
Simplest way to find these things is to ask Xcode to Analyze instead of Build. This will point to many memory problems like this, as well as many other issues.
This code is in reference to my comment below. Section should be a NSArray property. Then you write the getter like this:
-(void) sections {
if (!sections) {
<<code from dividesections that ends with...>>
self.sections = [NSArray arrayWithObjects: section1,section2,nil];
}
return sections;
}
That way you know sections is always correct, and don't have to worry about when you call divideSections.

How can i push a TableView on clicking a button and initialize the TableViewCell with NSDictionary?

Can anyone please help me about how to push a table view on clicking a button.
I want to load the messages from NSMutableArray to the table view cells and NSMutableArray is loaded with the data parsed from a URL..
-(IBAction)readMessages:(id)sender
{
// i want to push the tableview when clicking the button in relation with this method
// WHAT MUST I DO HERE?
}
Instead of asking a new question i liked to edit this one, since the matter is in the same aspect..
I now can create the tableview programatically, but i cant initialize its cells with the data i get from Json array. Here is my code:
NSString *str1=[#"?username=" stringByAppendingString:userNameField.text];
NSString *str2=[#"&password=" stringByAppendingString:passwordField.text];
NSString *str3=[str1 stringByAppendingString:str2];
NSString *str4 =[#"http://" stringByAppendingString:serverField.text];
NSURL *url=[NSURL URLWithString:[str4 stringByAppendingString:[#"/ipad/login.php" stringByAppendingString:str3]]];
//get the url to jsondata
NSData *jSonData=[NSData dataWithContentsOfURL:url];
if (jSonData!=nil) {
NSError *error=nil;
id result=[NSJSONSerialization JSONObjectWithData:jSonData options:
NSJSONReadingMutableContainers error:&error];
if (error==nil) {
NSDictionary *mess=[result objectForKey:#"message"];
NSDictionary *messContent=[mess valueForKeyPath:#"message"];
NSDictionary *messID=[mess valueForKeyPath:#"ID"];
NSString*key1=[ result objectForKey:#"key" ];
NSString *s1=[#"http://" stringByAppendingString:serverField.text];
NSString *s2=[s1 stringByAppendingString:#"/ipad/button.php"];
NSURL *url2=[NSURL URLWithString:[s2 stringByAppendingString:[#"?key=" stringByAppendingString:key1]]];
NSData *data2=[NSData dataWithContentsOfURL:url2];
id result2=[NSJSONSerialization JSONObjectWithData:data2 options:NSJSONReadingMutableContainers error:nil];
mesID = [NSMutableArray array];//saving meesage ID s to NSMutableArray
content = [NSMutableArray array];
// i logged here and it saves the data, now i want to display my data in table view
for (NSDictionary *data in mess) {
[mesID addObject:[data objectForKey:#"ID"]];
[content addObject:[data objectForKey:#"message"]];
[[NSUserDefaults standardUserDefaults] setObject:messID forKey:#"message"];
[[NSUserDefaults standardUserDefaults] setObject:messContent forKey:#"messContent"];
//messID will be saved as the Title of the cells and messContent will be displayed as the text area of that cell, opening in a new view
And this is the output, i want to set the titles of cells as ID and their content as text:
2012-01-17 16:26:59.873 ipad_Teslim[940:f803] MessID: (
1,
3
)
2012-01-17 16:26:59.875 ipad_Teslim[940:f803] Content: (
asdf,
"this is a test"
)
As i have mentioned in my code too, messID will be saved as the Title of the cells and messContent will be displayed as the text area of that cell, opening in a new view.. How can i do it now? Please Help me, there are a lot of tutorials there, i looked a lot too but couldn't break this problem.
Try this :
-(IBAction)readMessages:(id)sender {
SecondView *secondView =[[SecondView alloc] initWithNibName:#"SecondView" bundle:nil];
[self presentModalViewController:secondView animated:YES];
}
SecondView is your UIViewController subclass which hold a UITableView.
Q1: U no need to add a navigation to return back to ur main page.
When ever u use
[self.navigationController pushViewController:next animated:YES];
by defaults it will creates back navigation in the next view to push return back.
in case it doesn't created yet, Try the following code in next view:
- (void)viewDidLoad
{
[super viewDidLoad];
//To set the back buttin on leftside of Navigation bar
UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleDone target:self action:#selector(backclick:)] autorelease];
self.navigationItem.leftBarButtonItem = backButton;
}
- (IBAction)backclick:(id)sender //first declrared in .h file
{
// To goback to the previous view
[self.navigationController popViewControllerAnimated:YES];
}
If u have a navigation control and if u want to pushed by navigation try the following:
-(IBAction)readMessages:(id)sender {
NextView *next = [[NextView alloc]initWithNibName:#"NextView" bundle:nil];
[self.navigationController pushViewController:next animated:YES];
[next release];
}
if u dont have a navigation control and if u want to just display the next view, try the following:
-(IBAction)readMessages:(id)sender {
NextView *next =[[NextView alloc] initWithNibName:#"NextView" bundle:nil];
[self presentModalViewController:next animated:YES];
[next release];
}
if u are having sub view in the same class try the following:
-(IBAction)readMessages:(id)sender {
[self.view addsubview nextView];
}
Yes U can,
try this to create xib programitically in viewDidload:
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(10, 10,300,460)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];
But better to create by using
following path in xcode menu:
File-> New -> NewFile -> UIViewControllerSubClass -> Next -> Next -> Create
Or simply Drag & drop an view from ur Interface Builder
Q2: U can initialize ur tableView cells with JSONArray:
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [JSONarray count]; //***********
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = [JSONarray objectAtIndexIndexPath.row]; //***********
}