I have an array of dictionaries in an iOS .plist structured similar to the following:
<plist version="1.0">
<array>
<dict>
<key>name</key>
<string>Afghanistan</string>
<key>government</key>
<string>Islamic Republic</string>
<key>population</key>
<integer>29121286
</integer>
</dict>
<dict>
<key>name</key>
<string>Albania</string>
<key>government</key>
<string>Emerging Democracy</string>
<key>population</key>
<integer>2986952</integer>
</dict>
I am trying to load the <key>name</key> from each dictionary into an NSTableViewCell then display them all alphabetically in an NSTableView similar to the Contacts App in iOS.
Below are my ViewControllers .h and .m. The sort is working, but I am not able to load the results into the TableViewCells?
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
{
NSArray *sortedCountries;
}
#property (nonatomic, retain) NSArray *sortedCountries;
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize sortedCountries;
-(void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"countries"ofType:#"plist"];
NSArray *countries = [NSArray arrayWithContentsOfFile:path];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
NSArray *sortedCountries = [[countries sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *country = [sortedCountries objectAtIndex:indexPath.row];
NSString *countryName = [country objectForKey:#"name"];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = countryName;
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[sortedCountries release];
[super dealloc];
}
#end
EDIT: Another question related to this here.
Add an ivar to your view controller's #interface in the header file:
#interface MyViewController : UITableViewController
{
...
NSArray *sortedCountries;
}
Add this code (to read and sort the plist by country name) to your view controller's initWith... method:
NSArray *countries = [NSArray arrayWithContentsOfFile: pathToPlist];
// Now the array holds NSDictionaries, sort 'em:
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
sortedCountries = [[countries sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
Then use the following snippet to extract the values:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *country = [sortedCountries objectAtIndex:indexPath.row];
NSString *countryName = [country objectForKey:#"name"];
NSString *governmentType = [country objectForKey:#"government"];
NSSInteger population = [[country objectForKey:#"population"] integerValue];
// ... do something with countryName, governmentType, population
}
Don't forget to release sortedCountries:
- (void)dealloc
{
...
[sortedCountries release];
[super dealloc];
}
Create an NSArray for your file:
NSArray *iOSPlist = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"iOS" ofType:#"plist"]];
then in this method write after if (cell == nil){
}:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textLabel.text = [[iOSPlist objectAtIndex:indexPath.row] objectForKey:#"name"];
}
and don't forget to return [iOSPlist count] in the - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method;
Here is an example pulling the version number out of the info.plist. Use something similar to pull out your name key ( objectForKey:#"name")
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Info.plist"];
plist = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
NSString* version = [plist objectForKey:#"CFBundleVersion"];
Here's a StackOverflow question on working with data in plists. The answers get quite detailed.
Parse Plist (NSString) into NSDictionary
Related
In my app I parse an XML file then I would like to show the entry of this file in a UITableView. I found on the web how to make section in alphabetical order (like iPhone contacts), it works fine with my app. When i tap on a row in my tableview I want to display another ViewController, in which I will find some information about the row I tapped, but I'm having some problem: when I tap on a row the variable indexPath.row refers to the section and the information in new view controller aren't right. I will post here some screenshot to show you want I'm trying to explain.
In the following pictures you can see how the app should work:
In the following pictures you can see the error of my app:
You can see that in picture 1 the name is the same and in picture 2 you can see that the name it's wrong. I guess it depend on the variable indexPath.row. I will post here the code to create and populate the tableview:
#import "TableWasteViewController.h"
#import "WasteXmlParser.h"
#import "WasteDetailViewController.h"
#interface TableWasteViewController ()
#property(nonatomic,strong)NSArray *arrayWastes;
#property(nonatomic,strong)NSMutableArray *typeOfWaste;
#property(nonatomic,strong)NSMutableArray *typeOfBin;
#property(nonatomic,strong)NSMutableArray *indexWastes;
#property(nonatomic,strong)NSMutableArray *typeOfWasteBackup;
#end
#implementation TableWasteViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
WasteXmlParser *parser = [[WasteXmlParser alloc]init];
[parser parseWasteXml];
self.arrayWastes = [[NSArray alloc]init];
self.arrayWastes = [parser.arrayWastes mutableCopy];
self.indexWastes = [[NSMutableArray alloc]init];
self.typeOfWaste = [[NSMutableArray alloc]init];
self.typeOfBin = [[NSMutableArray alloc]init];
for (int i = 0; i < [self.arrayWastes count]; i++) {
[self.typeOfWaste addObject:[[self.arrayWastes objectAtIndex:i] objectForKey:#"type"]];
[self.typeOfBin addObject:[[self.arrayWastes objectAtIndex:i]objectForKey:#"place"]];
}
for (int i = 0; i < [self.typeOfWaste count]-1; i++) {
char alphabet = [[self.typeOfWaste objectAtIndex:i] characterAtIndex:0];
NSString *uniChar = [NSString stringWithFormat:#"%c", alphabet];
if (![self.indexWastes containsObject:uniChar]) {
[self.indexWastes addObject:uniChar];
}
}
self.typeOfWasteBackup = [self.typeOfWaste mutableCopy];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.indexWastes count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.indexWastes objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *alphabet = [self.indexWastes objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#",alphabet];
NSArray *wastes = [self.typeOfWaste filteredArrayUsingPredicate:predicate];
return [wastes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UIFont *myFont = [UIFont fontWithName:#"Arial" size:14.0];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *alphabet = [self.indexWastes objectAtIndex:[indexPath section]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
NSArray *wastes = [self.typeOfWaste filteredArrayUsingPredicate:predicate];
if ([wastes count] > 0) {
NSString *cellValue = [wastes objectAtIndex:indexPath.row];
cell.textLabel.font = myFont;
cell.textLabel.numberOfLines = 2;
cell.textLabel.text = cellValue;
}
return cell;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.indexWastes;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSIndexPath *indexPath = [self.tableWaste indexPathForSelectedRow];
WasteDetailViewController *vc = segue.destinationViewController;
vc.typeOfWaste = [self.typeOfWaste objectAtIndex:indexPath.row];
vc.typeOfBin = [self.typeOfBin objectAtIndex:indexPath.row];
vc.urlPic = [self.arrayWastes[indexPath.row]objectForKey:#"imgUrl"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)backToHome:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
I hope you can help me to fix this issue. Thank you
Rows are indexed by section, i.e. the first item in each section has indexPath.row == 0. So in order to look up values in your flattened vc.typeOfWaste and vc.typeOfBin arrays, you're going to need to do something along the lines of your numberOfRowsInSection method, where you filter the flattened array by the alphabet character and then get the item of the filtered array using indexPath.row.
Overall, this approach seems rather messy, having to filter your data repeatedly. Your data structures don't map well to the problem being solved. I would recommend using the TLIndexPathTools data model TLIndexPathDataModel because it's specifically designed for tables and collection views, can organize your data into sections, and can look up items by index path. Would be happy to walk you through a refactor if you like.
It's because the arrays that you're using to pass the data to the WasteDetailViewController, which are "typeOfWaste, typeOfBin, and urlPic" aren't sorted. The sorted array is called "wastes", but it is only available within the numberOfRowsInSection and the cellForRowAtIndexPath methods. You need to be passing the data in the wastes array forward, so rather than sorting the wastes array all the time, just sort it once after you've loaded it.
Add this property:
#interface TableWasteViewController ()
#property (strong, nonatomic) NSArray *sortedWastes;
#end
Now in viewDidLoad
NSString *alphabet = [self.indexWastes objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#",alphabet];
self.sortedWastes = [self.typeOfWaste filteredArrayUsingPredicate:predicate];
Finally, in prepareForSegue
vc.typeOfWaste = [self.sortedWastes objectAtIndex:indexPath.row];
Your problems all stem from the fact that you are displaying a sorted array, but the array that you are using to pass the data forward is the unsorted array, so the indexPath is completely useless.
Furthermore, your typeOfBin and urlPic are going to be wrong as well. You need to find some way to link all three of your arrays together, so that when you sort one, you sort them all. The method above only keeps your typeOfWaste array sorted.
I also faced this issue few days ago...u need to make one class set properties whatever you want and then add that object in one array after that u can sort array on one property then whole array will be sorted
#import <Foundation/Foundation.h>
#interface HomeFeed : NSObject
#property (nonatomic, copy) UIImage *ItemImage;
#property (nonatomic, copy) NSString *ItemTitle;
#property (nonatomic, copy) NSString *ItemDate;
#property (nonatomic, copy) NSString *ItemDescription;
#property (nonatomic, copy) NSString *ItemHours;
#property (nonatomic, copy) NSString *ItemID;
#property (nonatomic, copy) NSString *itemDetailUrl;
#property (nonatomic, copy) NSString *itemPerson;
#property (nonatomic, copy) NSString *itemThumbUrl;
#property (nonatomic, assign) int ItemDuration;
#end
#import "HomeFeed.h"
#implementation HomeFeed
#synthesize ItemTitle=_ItemTitle, ItemDate=_ItemDate, ItemImage=_ItemImage,ItemID=_ItemID,ItemDuration=_ItemDuration,ItemDescription,ItemHours=_ItemHours,itemDetailUrl,itemPerson,itemThumbUrl;
#end
NSArray*arr=[responseString JSONValue];
NSLog(#"Json Dictionary speakersss : %#",arr);
NSLog(#"Json arr count speaker : %i",arr.count);
for (int i=0; i<arr.count; i++) {
NSDictionary *dict=[[ NSDictionary alloc]init];
dict=[arr objectAtIndex:i];
HomeFeed *feed = [[HomeFeed alloc] init];
feed.ItemTitle = [NSString stringWithFormat:#"%#%#%#",[dict objectForKey:#"firstName"],#" ",[dict objectForKey:#"lastName"]];
feed.ItemDuration = [[NSString stringWithFormat:#"%#", [dict objectForKey:#"count"]] intValue];
feed.itemDetailUrl=[dict objectForKey:#"detailsUrl"];
[self.itemsDataArray addObject:feed];
[itemTitle addObject:feed.ItemTitle];
}
HomeFeed *feed = [[HomeFeed alloc] init];
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:#"ItemTitle" ascending:YES];
[self.itemsDataArray sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SpeakersCustomCell"];
cell.selectionStyle = NO;
cell.accessoryType = NO;
HomeFeed *feed = [self.itemsDataArray objectAtIndex:indexPath.row];
UIImageView *arrow = (UIImageView *)[cell viewWithTag:3];
arrow.image = [UIImage imageNamed:#"accessory.png"];
UILabel *lblLeft = (UILabel *)[cell viewWithTag:1];
UILabel *lblRight = (UILabel *)[cell viewWithTag:2];
lblLeft.text=feed.ItemTitle;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
HomeFeed *feed=(HomeFeed*)[self.itemsDataArray objectAtIndex:indexPath.row];
self.onlinDetail.strUrl=feed.itemDetailUrl;
NSString *key = #"OrientationStringValue";
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:feed.itemDetailUrl forKey:key];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotifSpeaker1" object:nil userInfo:dictionary];
}
I've been learning Objective C and the iPhone SDK gradually, and my current project involves building an application that stores numerical data (sports data). This is primarily for learning purposes, as there are multiple apps that do the same thing. Anyway, I've hit a bit of a snag. My intention is to have a list of players stored in a table and allow the user to add additional players.
At the moment, I have a button that when pressed, "Ninjas" will be added to the table. I've also enabled deleting in the table. Unfortunately, I can't seem to figure out how to save and load the data from a plist. I followed various tutorials and guides, but I can't figure out what's happening. My suspicion is I am loading the data from an empty array and adding to that array, but the array involving the data is a separate array from the plist. Unfortunately, I am a bit lost beyond that.
The data in my array is erased whenever I switch views. However, I've noticed that the data remains if I leave and come back, but not if I leave for a considerable amount of time, leave and restart the iphone, etc. This seems to occur even for apps that I have not worked on saving. Is this just a function of the iPhone holding onto data in case a user accidentally exits a program?
Hopefully I explained my issue somewhat tangibly. TL:DR version: I want to add data to an array, save it to a plist, and reload the data from the plist whenever the array is present on the screen. Code below is attempting to accomplish this, but it isn't succeeding.
Thanks
#import "RootViewController.h"
#import "NewPlayer.h"
#import "OptionsMenu.h"
#implementation RootViewController
#synthesize createdPlayers;
#synthesize listOfPlayers;
-(NSString *) pathOfFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
return [documentFolder stringByAppendingFormat:#"myfile.plist"];
}
-(void)applicationWillTerminate:(NSNotification*)notification{
NSMutableArray *array = [[NSMutableArray alloc]init];
[array writeToFile: [self pathOfFile] atomically:YES];
}
- (void)viewDidLoad
{
NSString *filePath = [self pathOfFile];
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
listOfPlayers.array = [array objectAtIndex:0];
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
[super viewDidLoad];
listOfPlayers = [[NSMutableArray alloc] init];
}
-(IBAction)AddButtonAction:(id)sender{
[listOfPlayers addObject:#"Ninjas"];
[createdPlayers reloadData];
}
-(IBAction)switchView:(id)sender{
OptionsMenu *second = [[OptionsMenu alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
}
-(IBAction)newView:(id)sender{
NewPlayer *second = [[NewPlayer alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated: YES];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listOfPlayers count];
}
// Customize the appearance of table view cells.
- (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];
}
// Configure the cell.
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:15];
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.textAlignment = UITextAlignmentCenter;
NSString *cellValue = [listOfPlayers objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSLog(#"delete section: %d rol: %d", [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]);
[listOfPlayers removeObjectAtIndex:[indexPath indexAtPosition:1]];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert)
{
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
-(void)tableView:(UITableView *)listOfPlayers moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath{
}
-(void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc{
[createdPlayers release];
[listOfPlayers release];
[super dealloc];
}
#end
#import <UIKit/UIKit.h>
#interface RootViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView* createdPlayers;
IBOutlet UIButton* superCat;
NSMutableArray *listOfPlayers;
}
#property(nonatomic, retain) IBOutlet NSObject *listOfPlayers;
-(NSString *) pathOfFile;
-(void)applicationWillTerminate:(NSNotification*)notification;
-(IBAction)AddButtonAction:(id)sender;
-(IBAction)switchView:(id)sender;
-(IBAction)newView:(id)sender;
#property (nonatomic, retain) UITableView* createdPlayers;
#end
Code update 20th Dec:
-(NSString *) pathOfFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
return [documentFolder stringByAppendingFormat:#"myfile.plist"];
}
-(void)applicationWillTerminate:(NSNotification*)notification
{
[self.listOfPlayers writeToFile:[self pathOfFile] atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}
- (void)viewDidLoad
{
self.listOfPlayers = [[NSMutableArray alloc] init];
NSString *filePath = [self pathOfFile];
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
self.listOfPlayers = array;
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
[super viewDidLoad];
}
I don't know plist. But you can try use NSData
NSArray *array;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
//Write data
[data writeToFile:filePath atomically:YES];
//Read data
NSFileManager *fileManager = [NSFileManager defaultManager];
NSData *oldData = [fileManager contentsAtPath:filePath];
NSArray *newArray = [NSKeyedUnarchiver unarchiveObjectWithData:oldData]
Welcome to the world of iOS programming. It's a lot of fun, but it can be frustrating, just as programming in any language can be. I feel your pain.
There are a number of things that I see:
In viewDidLoad, it appears that your code to read your file into an array is okay, but then it appears as if you're trying to assign only the first row of this array to your listOfPlayers. Try:
self.listOfPlayers = array;
array is an array, and so is your listOfPlayers. Also, using self will ensure that the synthesized setter is used, thus retaining your listOfPlayers when you release array.
In applicationWillTerminate, I believe you wish to write your listOfPlayers to a file, but what you are actually doing is allocating and initializing an empty array, and writing THAT to your file. Try:
-(void)applicationWillTerminate:(NSNotification*)notification
{
[self.listOfPlayers writeToFile: [self pathOfFile] atomically:YES];
}
I hope this helps!
There seems something wrong in your viewDidLoad
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
listOfPlayers.array = [array objectAtIndex:0];
.
.
}
.
.
.
listOfPlayers = [[NSMutableArray alloc] init];
You are accessing listOfPlayers before ever allocating it.
listOfPlayers.array is wrong as well
When I make an object class conform to NSCoding, the tableView does not show new entries. Suspected it had to do with the object initialization but cannot solve it. Might be very basic but cannot find the error. Here is the code I simplified to post:
Custom Object:
// DataObject.h
#import <Foundation/Foundation.h>
#interface DataObject : NSObject {
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
// DataObject.m
#import "DataObject.h"
#implementation DataObject
#synthesize name;
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) return nil;
name = [[decoder decodeObjectForKey:#"name"] retain];
return self;
}
These is the TableView root controller - Have omitted a few methods:
// RootViewController.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController {
NSMutableArray *list;
}
#property (nonatomic, retain) NSMutableArray *list;
- (void)add:(id)sender;
- (NSString *)dataFilePath;
#end
// RootViewController.m
#import "RootViewController.h"
#import "DataObject.h"
#implementation RootViewController
#synthesize list;
- (void)add:(id)sender
{
DataObject *newEntry = [[DataObject alloc] init];
newEntry.name = #"Willy";
[self.list addObject:newEntry];
[self.tableView reloadData];
// Scroll table view to last row
if ([list count] > 1) {
NSUInteger index = list.count - 1;
NSIndexPath *lastRow = [NSIndexPath indexPathForRow:index inSection: 0];
[self.tableView scrollToRowAtIndexPath: lastRow atScrollPosition: UITableViewScrollPositionTop animated: YES];
}
[newEntry release];
}
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"datafile"];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.title = #"Names";
self.list = [[NSMutableArray alloc] init];
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithTitle:#"Add"
style:UIBarButtonItemStylePlain
target:self
action:#selector(add:)];
self.navigationItem.leftBarButtonItem = addButton;
[addButton release];
[self.tableView reloadData];
}
- (void)applicationWillResignActive:(NSNotification *)notification;
{
[NSKeyedArchiver archiveRootObject:self.list toFile:[self dataFilePath]];
}
- (void)viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
[super viewWillAppear:animated];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [list 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];
}
// Configure the cell.
NSUInteger row = [indexPath row];
DataObject *oneName = [self.list objectAtIndex:row];
cell.textLabel.text = oneName.name;
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
NSUInteger row = [indexPath row];
[self.list removeObjectAtIndex:row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#end
Oh, man I am so embarrassed...It had nothing to do with NSCoding directly but when I made the data class NSCoding compliant I added in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
The first line is fine, but the second assigned a new pointer address to self.list and the tempArray was later autoreleased...
As this line was not executed in the "not NSCoding compliant" version it appeared to work then and not when encodeWithCoder and initWithCoder were implemented.
Because of this self.list was nil instead of being initialized and ready when in the add: method I added a new instance. It was created fine, but never added to the array and therefore never showed in the tableView.
This was easily fixed using instead in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
[self.list addObjectsFromArray:tempArray];
Worst part is that I was pulling my hair because of this and was convinced it was because of NSCoding and the initialization of objects...
Whenever i scroll my UITableView, it looks at an array to find out what to fill the next cell with, but the array is empty, causing a crash, and appears to have been released somehow. Here is my code:
.h
#interface HomeViewController : UITableViewController {
NSArray *vaults;
}
#property (retain) NSArray *vaults;
#end
.m
#import "HomeViewController.h"
NSString *vaultsPath;
#implementation HomeViewController
#synthesize vaults;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSFileManager *fileManager = [NSFileManager defaultManager];
self.vaults = [fileManager contentsOfDirectoryAtPath:vaultsPath error:nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.vaults 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];
}
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.vaults objectAtIndex:indexPath.row]]; //Crashes at this line, with the self.vaults array now empty.
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
return cell;
}
- (void)dealloc
{
[super dealloc];
[self.vaults release];
}
#end
Any ideas?
My guess is the app crashes when you try to access the value of vaultsPath, which must be deallocated. Because the table view row count is based on the number of elements in the array, the method returning the cells, won't be called if there isn't any element in.
Try to retain the value assigned to vaultsPath, and don't forget to release it later.
Thank you for looking into my question here. I am trying to get uitableview sectioned by dates in descending order. I get the data from the sqllite order by datefield desc. But whatever I do the dates are displayed in ascending order. I have the following set of data that comes out of the db and in this order:
ID BookName DateRead
1 ABC 19-10-2011
2 ABZ 27-06-2011
3 ABD 28-05-2011
I would like the data to appear like the following
19-10-2011
ABC
27-06-2011
ABZ
28-05-2011
ABD
but no matter what I am trying I am getting the data returned as below:
19-10-2011
ABC
28-05-2011
ABZ
27-06-2011
ABD
Here is the complete list of code that I am using:
.h file
#import <UIKit/UIKit.h>
#interface BookHistoryViewController : UITableViewController {
NSArray *books;
NSMutableDictionary *sections;
}
#property (nonatomic,retain) NSArray *books;
#property (nonatomic,retain) NSMutableDictionary *sections;
#end
Here is my .m file
- (void)viewDidLoad {
TestAppDelegate *appDelegate = (TestAppDelegate *)[[UIApplication sharedApplication] delegate];
[Book getInitialDataToDisplay:[appDelegate getDBPath]];
self.books = [NSMutableArray arrayWithArray:appDelegate.bookArray];
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
// Loop through the books and create our keys
for (NSDictionary *book in books)
{
NSString *c = [book valueForKey:#"DateRead"];
found = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc] init] forKey:c];
}
}
// Loop again and sort the books into their respective keys
for (NSDictionary *book in self.books)
{
[[self.sections valueForKey:[book valueForKey:#"DateRead"]] addObject:book];
}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{
[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"DateRead" ascending:NO]]];
}
[super viewDidLoad];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.sections allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[self.sections allKeys] sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:section]] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
NSDictionary *book = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = [book valueForKey:#"title"];
return cell;
}
When you get the book in tableView:cellForRowAtIndexPath:, you first sort by section key which is the dateRead as a string, but you sort in ascending order. You probably want to sort using a descriptor like you do in viewDidLoad. In fact, why not just keep it in the correctly sorted order so that you don't have to sort each time you need a cell? The way you are doing it will cause the interface to grind to a halt pretty quickly.