I have an indexed tableView that displays a list of songs from the user's iPod library. This is how I currently get the song titles, which causes scrolling to be very slow:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.text= [self titleForRow:indexPath]; //getting cell content
}
...which calls these methods:
-(NSString *)titleForRow:(NSIndexPath *)indexpath{
NSMutableArray* rowArray=[[NSMutableArray alloc]initWithCapacity:0];
rowArray=[self getArrayOfRowsForSection:indexpath.section];
NSString *titleToBeDisplayed=[rowArray objectAtIndex:indexpath.row];
return titleToBeDisplayed;
}
-(NSMutableArray *)getArrayOfRowsForSection:(NSInteger)section
{
NSString *rowTitle;
NSString *sectionTitle;
NSMutableArray *rowContainer=[[NSMutableArray alloc]initWithCapacity:0];
for (int i=0; i<self.alphabetArray.count; i++)
{
if (section==i) // check for right section
{
sectionTitle= [self.alphabetArray objectAtIndex:i]; //getting section title
for (MPMediaItem *song in songs)
{
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
rowTitle= [title substringToIndex:1]; //modifying the statement to its first alphabet
if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title
{
[rowContainer addObject:title]; //adding the row contents of a particular section in array
}
}
}
}
return rowContainer;
}
The problem is: for each cellForRowAtIndexPath, I am calling these methods and creating these arrays for each cell. So I need to create a separate array and simply call the objectAtIndex from that array.
This is what I have tried so far: created an NSMutableArray *newArray, and the following method:
-(NSMutableArray *)getNewArray:(NSInteger)section
{
NSString *rowTitle;
NSString *sectionTitle;
for (int i=0; i<self.alphabetArray.count; i++)
{
if (section==i) // check for right section
{
sectionTitle= [self.alphabetArray objectAtIndex:i]; //getting section title
for (MPMediaItem *song in songs)
{
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
rowTitle= [title substringToIndex:1]; //modifying the statement to its first alphabet
if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title
{
[newArray addObject:title]; //adding the row contents of a particular section in array
}
}
}
}
return newArray;
}
But this seems very wrong and I'm really confused on how to solve this. I have no clue how to create a separate array with these methods, populating newArray with the song titles, and I've searched Google for quite some time now and I can't find anything that would help me.
Could somebody point me in the right direction, or please show me how I'd creat newArray? I've attached my tableView data source here. Thanks. Any help would be much appreciated.
Table View cells are enqueued and dequeued in a serial manner. Leveraging this ability you should expect that only sections change in less frequency. You can use static variables to exempt the fetching of a section. Look at my code
-(NSString *)titleForRow:(NSIndexPath *)indexpath{
static NSIndexPath *funIndexPath;
static NSMutableArray *funSectionArray;
if (!funIndexPath || funIdexpath.section != indexpath.section) {
funIndexPath = indexpath;
funSectionArray = [self getArrayOfRowsForSection:indexpath.section];
}
rowArray = funSectionArray;
NSString *titleToBeDisplayed = [rowArray objectAtIndex:indexpath.row];
return titleToBeDisplayed;
}
This is the first optimisation possible.
When I look at your fetching results array, some things are not clear. Why are you iterating to check section == i. You can just substitute section for i in your code.
-(NSMutableArray *)getNewArray:(NSInteger)section {
NSString *rowTitle;
NSString *sectionTitle;
sectionTitle = [self.alphabetArray objectAtIndex:section]; //getting section title
for (MPMediaItem *song in songs) {
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
rowTitle= [title substringToIndex:1]; //modifying the statement to its first alphabet
if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title
{
[newArray addObject:title]; //adding the row contents of a particular section in array
}
}
return newArray;
}
Its still not clear what you are trying to achieve here.
Going against my own answer, I suggest you that you do not compute you data for display during view rendering time. You need to create the cells as fast as possible. Why not create a property or an iVar (like #bauerMusic is Suggesting) and compute the date in viewDidLoad or declare a custom function which will reload your property (possible an Array or Dictionary) and while rendering the UI, just set the values (in you viewForIndexPath)?
You can use an array of two level depth to store for maintaining sections and rows. i.e. Level 1 - Sections and Array of rows, Level 2 - row.
The center of it all is:
cell.textLabel.text = [self titleForRow:indexPath];
Now, what you're actually using are NSString. Simple. Instead, you're creating a new array for each cell each call??
Why not have one array, say an NSMutableArray. Have it as a #property or local iVar.
#property (strong, nonatomic) NSMutableArray *rowArray;
// Instantiate in -viewDidLoad
rowArray = [NSMutableArray array]; // Much more elegant IMHO
Now call a method that do all the content loading, but do it ONCE. You can always call it again to refresh.
Something like:
// In -viewDidLoad
rowArray = [NSMutableArray array];
[self loadArrayContent];
Then, your cellForRowAtIndexPath should only be using your preloaded rowArray and simply pulling NSString titles from it.
My general way to handle a table view is to create (during table view setup) an NSMutableArray for the rows (anchored in the data source object). Each entry in the array is an NSMutableDictionary that is "primed" with the basic stuff for the row, and if more info needs to be developed to display the row, that is cached in the dictionary. (Where there are multiple sections I use an array of sections containing arrays of rows.)
What I might do for a play list is initialize the array with empty dictionaries, then, as one scrolls, access each play list element and cache it in the dictionary.
In fact, if one really wanted, updating the dictionary could be done via a background task (with appropriate locking) so that scroll speed wouldn't be hampered by the lookup operations.
Related
I Have a CSV File that contain 6000 rows.
I would like to Populate those 6000 rows in a TableView Then Use a SearchBar to fetch a single row
I used CSVParser but it needs 10 minutes to load the UITableview:
-(void)ViewDidLoad{
CSVParser *parser = [CSVParser sharedInstance];
NSDictionary *dictionary = [parser parseCSVFile: #"ports"];
NSArray* rows = [dictionary allKeys];
}
Then to Populate the tableview:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 25;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
PortListCell *cell = (PortListCell *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.Codeport.text = [[rows objectAtIndex:indexPath.row]valueForKey:#"ident"];
cell.Nameport.text = [[rows objectAtIndex:indexPath.row]valueForKey:#"name"];
NSString*CodeCountry = [[[rows objectAtIndex:indexPath.row]valueForKey:#"iso_country"]lowercaseString];
cell.ImageCountry.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#.png",CodeCountry]];
return cell;
}
What is the best way to populate a UITableView with these rows ?
Loading large amounts of data into table views, which will never show the whole amount of data anyway, is commonly done asynchronously, with periodic updates. If you can get into the CSV parser, perhaps it could add rows to the array and call tableview reloadData every 10 rows or something. That way your slow parsing and tableview update would happen in the background, as the table view was already appearing active to the user.
You can use SQLite to import all the data into DB, and use it for more efficiency. I am showing you using FMDB.
The way you are parsing your data, using CSVParser you read the CSV, and insert into DB dynamically.
You can create your insert sql query dynamically using the function below
-(NSDictionary *)createInsertSQLWithValue:(NSDictionary *)value forTable:(NSString *)tableName{
NSString *cols=#"";
NSMutableArray *vals=[[NSMutableArray alloc] init];
NSString *placeHolders=#"";
NSArray *keys=[value allKeys];
for(NSString *key in keys){
[vals addObject:[value objectForKey:key]];
placeHolders=[placeHolders stringByAppendingFormat:#"?,"];
}
//Remove extra ","
if(cols.length>0){
cols=[cols substringToIndex:cols.length-1];
placeHolders=[placeHolders substringToIndex:placeHolders.length-1];
}
NSString *sql=[NSString stringWithFormat:#"insert into %# (%#) values (%#)",tableName,cols,placeHolders];
return #{#"sql":sql,#"args":vals};
}
You just need to do
-(void)populateData{
CSVParser *parser = [CSVParser sharedInstance];
NSArray *ports = [parser parseCSVFile: #"ports"];
for(NSDictionary *value in ports){
[self addPort:value];
}
}
-(void)addPort:(NSDictionary*)value{
//say you created a table ports
NSDictionary *sql=[self createInsertSQLWithValue:value forTable:#"ports"];
BOOL inserted=[self.db executeUpdate:[sql objectForKey:#"sql"] withArgumentsInArray:[sql objectForKey:#"args"]];
if(inserted){
NSLog(#"Port Inserted");
}
}
Note: Make sure you do this when you need to populate the data, if required once, do it once. And too you need to create table with the exact column name as keys in the dictionary too.
Cheers.
I want to sort objects that i created & stored in NSMutableArray in AppDelegate.m.
Stations is NSObject Class
I want to show station names in another UIViewController in alphabet order(in UITableViewCell) & when i click on them i want to pass the object that contains station name,latitude,longitude to next UIViewController
Currently i have extracted station name from stationList(Global NSMutableArray) to another NSMutableArray on UIViewControllers Cell & sorted it via
[sortedArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
but when didSelectRowAtIndexPath is being called i have to get this name from cell & search it in the stationList array to pass lat,long which is not good i think.
stationList Array Log(It has 100 objects):-
<__NSArrayM 0x79a2f110>(
<Stations: 0x78743540>,
<Stations: 0x78743630>,
<Stations: 0x78743670>,
<Stations: 0x78743750>,
<Stations: 0x78743830>,
<Stations: 0x78743910>,
<Stations: 0x78743a10>,
<Stations: 0x78743af0>
}
-(void)loadStations
{
stationList = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"stations" ofType:#"txt"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
// NSLog(#"%#",content);
NSArray *tempArr = [content componentsSeparatedByString:#"\n"];
for (int i =0; i<[tempArr count]; i++)
{
NSString *rawData = [tempArr objectAtIndex:i];
if (rawData !=nil)
{
Stations *newStation = [[Stations alloc]init];
NSArray *data = [rawData componentsSeparatedByString:#"\t"];
newStation.sId = i+1;
newStation.name = [NSString stringWithFormat:#"%#",[data objectAtIndex:0]];
newStation.latitude = [[data objectAtIndex:1] doubleValue];
newStation.longitude = [[data objectAtIndex:2] doubleValue];
[stationList addObject:newStation];
}
}
}
Suggest me good practice/way for this, or maybe use Dictionary?
I see two solutions here:
1) you can retrieve object from your stationList based on indexPath.row
- (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
Stations* station = stationsList[indexPath.row];
...
}
2) you can create custom UITableViewCell and store referenced object there:
#interface StationCell : UITableVIewCell
#property(weak) Stations* station;
#end
...
- (UITableViewCell*) tableView:(UITableVIew*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
StationCell* cell;
// dequeue StationCell
...
cell.station = stationList[indexPath.row];
}
...
- (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
StationCell* cell = [tableView cellAtIndexPath:indexPath];
Stations* station = cell.station;
...
}
I would choose between solutions based on complexity of data displayed in cell - using custom UITableViewCell gives oportunity to move configuration of cell from view controller to cell implementation.
edit
As far as sorting stationsList, you can use e.g.:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
stationsList = [stationsList sortedArrayUsingDescriptors:#[sort]];
I would advise against sorting an array of station names separate from your array stationList. Instead I would suggest sorting your stationList (or a copy of it if you only want to change the oder in the table view and need to maintain some other ordering elsewhere)
There are methods like sortUsingComparator: that takes comparator block as a parameter. You write a block that compares 2 elements in your array, and the method uses that block to figure out the ordering of your objects and sort the array. In your case it would simply be a matter of writing a block that compares the name properties of 2 station objects.
My tableview is populated with cells that show an item with its name, price, and brand. Some objects that I get from the web service return , and that looks ugly on the table view cell. I don't want to populate table view cells that have a price of "null". Here is my code so far. For now, I change it to say "Price Unavailable".
for (NSDictionary *objItem in resultsArray)
{
NSString *currentItemName = [objectTitlesArray objectAtIndex:indexPath.row];
if ([currentItemName isEqualToString:objItem[#"title"]])
{
if (cell.priceLabel.text != (id)[NSNull null] && cell.priceLabel.text.length != 0 && cell.brandLabel.text != (id)[NSNull null] && cell.brandLabel.text.length != 0)
{
cell.nameLabel.text = currentItemName;
NSDictionary *bestPageDictionary = objItem[#"best_page"];
NSNumber *price = bestPageDictionary[#"price"];
if ((cell.priceLabel.text = #"<null>"))
{
cell.priceLabel.text = #"Price Unavailable";
}
else
{
cell.priceLabel.text = [NSString stringWithFormat:#"$%#", price];
}
NSArray *brandsArray = objItem[#"brands"];
cell.brandLabel.text = [brandsArray firstObject];
}
}
}
This is terribly inefficient. You are keeping the JSON (I'm assuming) return in a dictionary, then looping over the dictionary for EVERY cell that you're creating. Not only that, you aren't cleaning up the JSON ahead of time.
It's much more expensive to create a useless cell then go back and try to delete it. In your numberOfRowsInSection delegate, you've already told the tableview that you have X many cells. Now you're trying to delete the cells which will mess up the callbacks. You'd have to create a method that will run after you finish creating all cells that will then loop through all your cells to delete them from the tableView, which will then call [table reloadData]. However, because you actually aren't removing the data from the NSDictionary, you actually will create the same amount of cells again and be stuck in an infinite loop.
Solution:
First, change your structure. Once you get the JSON return, sanitize it to delete all values with no price. I suggest also using an object class to hold each server object. This will simplify your code a lot for the TableView as well as other classes as well. Now that you've cleaned up your return, change it from a NSDictionary into a NSMutableArray. Then in numberOfRowsInSection: you call [array count]. In cellForRowAtIndexPath: you simply look at [array objectAtIndex:indexPath.row] to get your object.
You'll want code to generally look like:
ServerItem.h : NSObject{
#property (nonatomic,retain) NSString* name;
...
add in other properties here
}
- (NSMutableArray *) parseJSON:(NSDictionary *)jsonDict{
NSMutableArray *returnArray = [NSMutableArray array];
NSArray *dictArray = [NSArray arrayWithArray:jsonDict[#"results"]];
for (NSDictionary *itemDict in dictArray)
{
NSDictionary *bestPageDictionary = objItem[#"best_page"];
if (![bestPageDictionary[#"price"] isEqualToString:#"<null>"]])
{
ServerItem item = [ServerItem new];
item.price = ...
item.name = ....
[returnArray addObject:item];
}
}
return returnArray;
}
Inside your webService code:
self.itemArray = [self parseJSON:dataDictionary];
Then
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.itemCallArray count]
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
ServerItem *cellItem = [self.itemsArray objectAtIndex:indexPath.row];
cell.nameLabel.text = cellItem.name;
cell.priceLabel.text = cellItem.price;
...
}
I am trying to display sections and rows correctly for my uiTableView.
I have had great help from one contributor and am fairly close to fixing my issue. The Issue can be seen here. Its not far off being right, its just the sections that need to be sorted.
It is repeating the section titles instead of only showing it once. Im not sure exactly how to fix this.
// Find out the path of recipes.plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"lawpolice" ofType:#"plist"];
// Load the file content and read the data into arrays
self.dataArray = [NSArray arrayWithContentsOfFile:path];
//Sort the array by section
self.sortedArray = [self.dataArray sortedArrayUsingDescriptors:#[
[NSSortDescriptor sortDescriptorWithKey:#"Section" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"Title" ascending:YES]]];
self.temp = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in self.sortedArray) {
NSMutableArray *array = self.temp[dict[#"Section"]];
// No items with the same section key stored yet, so we need to initialize a new array.
if (array == NULL) {
array = [[NSMutableArray alloc] init];
}
// Store the title in the array.
[array addObject:dict[#"Title"]];
// Save the array as the value for the section key.
[self.temp setObject:array forKey:dict[#"Section"]];
}
self.policePowers = [self.temp copy]; // copy returns an immutable copy of temp.
//Section for sorting
self.sectionArray = [self.sortedArray valueForKeyPath:#"Section"];
NSLog(#"%#", self.sectionArray);
//Title
self.namesArray = [self.sortedArray valueForKeyPath:#"Title"];
//Offence
self.offenseArray = [self.sortedArray valueForKeyPath:#"Offence"];
//Points to Prove
self.ptpArray = [self.sortedArray valueForKeyPath:#"PTP"];
//Action
self.actionsArray = [self.sortedArray valueForKeyPath:#"Actions"];
//Notes
self.notesArray = [self.sortedArray valueForKeyPath:#"Notes"];
//Legislation
self.legislationArray = [self.sortedArray valueForKeyPath:#"Legislation"];
//PNLD
self.pnldArray = [self.sortedArray valueForKeyPath:#"PNLD"];
//Image
self.imageString = [self.sortedArray valueForKeyPath:#"image"];
titleForHeaderInSection
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [self.sectionArray objectAtIndex:section];
}
numberOfSectionsInTableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.policePowers count];
}
numberOfRowsInSection
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *sectionrows = self.policePowers[self.sectionArray[section]];
return [sectionrows count];
}
Update
To be clear, if two items have the same Section value, I want to automatically group them into an array and have that array mapped to the Section value at the end
NSDictionary dictionaryWithObjects:forKeys: basically loops through two arrays and maps the object in one array at the current index as the key for the object in the other array at the same index. When you're calling
self.policePowers = [NSDictionary dictionaryWithObjects:self.namesArray forKeys:self.sectionArray];
it therefore maps the items in self.sectionArray as the keys for the items in self.namesArray. Looking at your plist file, the "Title" keypath (which is mapped to self.namesArray) has a value of string, so your NSLog results make sense, as self.namesArray is an array of strings, not an array of arrays.
I'm not sure how you were supposed to get a result like
"Alcohol: Licensing/Drive unfit" = {
"Drive/attempt to drive/in charge whilst unfit or over",
"Drive/attempt to drive/in charge whilst unfit or over",
"Drive/attempt to drive/in charge whilst unfit or over",
}
Where is that array supposed to come from?
-- EDIT --
I don't think there's a concise way to accomplish what you want, so it'd have to be done manually. I haven't actually used [NSArray arrayWithContentsOfFile:path] before, so is self.dataArray an array of dictionaries with each item representing one of the items in the plist (Item 44, Item 45, etc)? If so, you could do something like this:
NSMutableDictionary *temp = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in self.dataArray) {
NSMutableArray *array = temp[dict[#"Section"]];
// No items with the same section key stored yet, so we need to initialize a new array.
if (array == null) {
array = [[NSMutableArray alloc] init];
}
// Store the title in the array.
[array addObject:dict[#"Title"]];
// Save the array as the value for the section key.
[temp setObject:array forKey:dict[#"Section"]];
}
self.policePowers = [temp copy]; // copy returns an immutable copy of temp.
-- EDIT AGAIN --
The app crashes because self.policePowers is an NSDictionary, not an NSArray; thus it doesn't have an objectAtIndex: method. If you're trying to get the section title, try this instead:
return [self.sectionArray objectAtIndex:section];
Furthermore, if you're working with a table view, I'd basically have self.sectionArray sorted whichever way you like, then whenever I needed to populate data in each section, I would use self.policePowers[self.sectionArray[section]] to return the array of titles mapped to that section title.
-- YET ANOTHER --
If you break it up into the following lines, where is the NSRangeException thrown? If you NSLog, do the results match what you expect?
NSString *title = self.sortedKeys[indexPath.section];
NSArray *array = self.policePowers[title];
NSString *value = array[indexPath.row];
I'm working in Storyboard but I presume now it's time to face the code...
I have a PHP file on my server which outputs the contents of my MySQL database table as an array in JSON format:
{
"id":"2",
"name":"The King's Arms",
"city":"London",
"day":"Tuesday",
}
I'll need all the data eventually, but for now I just want to output the city fields as a UITableView. How would I go about doing this?
I believe it was in the year 2012/2013 where Apple covered a fantastic video on code practices, one sub topic highlighted was a good smart way of handling JSON objects and creating data objects for them. I'm sorry I forgot the name of the actual video, if someone remembers it please do edit the answer for me.
What apple covered was to have a data object that stores each json object. We will then create an array to store these objects and access the required fields appropriately when populating our tableView. So in your case you would do something like this.
in your project navigator add a file of NSObject type and add the following code:
#import <Foundation/Foundation.h>
#interface PlaceDataObject : NSObject
-(id)initWithJSONData:(NSDictionary*)data;
#property (assign) NSInteger placeId;
#property (strong) NSString *placeName;
#property (strong) NSString *placeCity;
#property (strong) NSString *placeDay;
#end
and in your .m file you would add this code
#import "PlaceDataObject.h"
#implementation PlaceDataObject
#synthesize placeId;
#synthesize placeName;
#synthesize placeCity;
#synthesize placeDay;
-(id)initWithJSONData:(NSDictionary*)data{
self = [super init];
if(self){
//NSLog(#"initWithJSONData method called");
self.placeId = [[data objectForKey:#"id"] integerValue];
self.placeName = [data objectForKey:#"name"];
self.placeCity = [data objectForKey:#"city"];
self.placeDay = [data objectForKey:#"day"];
}
return self;
}
#end
What you have now is a data object class which you can use everywhere in your code where ever required and grab the appropriate details for whichever table youre showing, whether it be a city fields table or a city and name table etc. By doing this you will also avoid having json decoding code everywhere in your project. What happens when the name of your 'keys' changes? rather than scouring through your code correcting all your keys, you simply go to the PlaceDataObject class and change the appriopriate key and your application will continue working.
Apple explains this well:
"Model objects represent special knowledge and expertise. They hold an application’s data and define the logic that manipulates that data. A well-designed MVC application has all its important data encapsulated in model objects.... they represent knowledge and expertise related to a specific problem domain, they tend to be reusable."
Populating your array with custom objects for every json entry that comes in from the server
Now onto populating an array of this custom data object you've made. Now following the MVC approach, it's probably best that you have all your methods that process data in a different class, your Model class. That's what Apple recommends to put these kind of methods in a seperate model class where all the processing happens.
But for now we are just going to add the below code to the View Controller just for demonstration purposes.
Create a method in your view controller's m file that will process your JSON array.
//make sure you have a global NSMutableArray *placesArray in your view controllers.h file.
-(void)setupPlacesFromJSONArray:(NSData*)dataFromServerArray{
NSError *error;
NSMutableArray *placesArray = [[NSMutableArray alloc] init];
NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromServerArray options:0 error:error];
if(error){
NSLog(#"error parsing the json data from server with error description - %#", [error localizedDescription]);
}
else {
placesArray = [[NSMutableArray alloc] init];
for(NSDictionary *eachPlace in arrayFromServer)
{
PlaceDataObject *place = [PlaceDataObject alloc] initWithJSONData:eachPlace];
[placesArray addObject:place];
}
//Now you have your placesArray filled up with all your data objects
}
}
And you would call the above method like so:
//This is not what your retrievedConnection method name looks like ;)
// but you call the setupPlacesFromJSONArray method inside your connection success method
-(void)connectionWasASuccess:(NSData *)data{
[self setupPlacesFromJSONArray:data];
}
Populating your tableView with your custom Data objects
As for populating your data in your TableView you do so like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
//We check against table to make sure we are displaying the right number of cells
// for the appropriate table. This is so that things will work even if one day you
//decide that you want to have two tables instead of one.
if(tableView == myCitysTable){
return([placesArray count]);
}
return 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell)
{
//set your configuration of your cell
}
//The beauty of this is that you have all your data in one object and grab WHATEVER you like
//This way in the future you can add another field without doing much.
if([placesArray count] == 0){
cell.textLabel.text = #"no places to show";
}
else{
PlacesDataObject *currentPlace = [placesArray objectAtIndex:indexPath.row];
cell.textLabel.text = [currentPlace placeCity];
// in the future you can grab whatever data you need like this
//[currentPlace placeName], or [currentPlace placeDay];
}
return(cell);
}
Short disclaimer: the code above has not been tested, but please let me know if it all works well or if I've left out any characters.
If the raw data from you server arrives in a NSData object, you can use NSJSONSerialization class to parse it for you.
That is:
NSError *error;
NSMutableArray *cityArray = [[NSMutableArray alloc] init];
NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromServer options:0 error:error];
if(arrayFromServer)
{
NSLog(#"error parsing data - %#", [error localizedDescription]);
} else {
for(NSDictionary *eachEntry in arrayFromServer)
{
NSString *city = [eachEntry objectForKey:#"city"];
[cityArray addObject: city];
}
}
Once you're done populating your array of cities, this is what you can return in the table view's data source methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1; // your table only has one section, right?
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(section == 0)
{
return([cityArray count]);
}
NSLog(#"if we're at this place in the code, your numberOfSectionsInTableView is returning something other than 1");
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// are you using standard (default) cells or custom cells in your storyboard?
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell)
{
cell.text = [cityArray objectAtIndex:indexPath.row];
}
return(cell);
}
what is myCitysTable, Paven? is that suppose to be the name of the TableView? i'm struggling to figure out how to name it, if so...