I have some JSON data that I am getting from my database. I can pull it fine and load it into my table view. my issue is separating my JSON data so I can section the tableview.
JSON
[{"id":"1","name":"steve mans","phoneNumber":"(559)123-4455","showNumber":"1","greenCard":"1","expiration":"2014-02-15","driver":"1","paid":"1","watch":"1"},
{"id":"2","name":"myself and me","phoneNumber":"(559)321-6784","showNumber":"1","greenCard":"1","expiration":"2013-10-18","driver":"0","paid":"0","watch":"2"},
{"id":"4","name":"tod bellesmithson","phoneNumber":"(559)678-3421","showNumber":"0","greenCard":"1","expiration":"2013-11-22","driver":"1","paid":"0","watch":"2"},
{"id":"3","name":"John Smith","phoneNumber":"(559)123-1234","showNumber":"1","greenCard":"0","expiration":"2013-10-08","driver":"0","paid":"1","watch":"3"},
{"id":"5","name":"greg smith","phoneNumber":"559 345-1234","showNumber":"1","greenCard":"1","expiration":"2013-10-08","driver":"0","paid":"1","watch":"3"}]
What I am trying to do is, separate this data into three sections in my tableview. So I thought create three different tables and load each table into a different section of the tableview. But the information is the same in each one (id, name, phone etc.) So I ended up with one table and added a column that designates what shift people work, 'watch'. So how do I separate the data by using the watch column, so in my tableview i will have:
section one
people who work night shift
section two
people who work morning
section three
night shift
Try with this code:
NSArray *data = (NSArray)[NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&error];
NSMutableArray *morningShift = [NSMutableArray array];
NSMutableArray *noonShift = [NSMutableArray array];
NSMutableArray *nightShift = [NSMutableArray array];
for (int i=0; i< [data count]; i++)
{
NSDictionary *item = data[i];
if (#"1" == item[#"watch"] )
{
[morningShift addObject:item];
} else if (#"2" == item[#"watch"] )
{
[noonShift addObject:item];
} else if (#"3" == item[#"watch"] )
{
[nightShift addObject:item];
}
}
try this
NSMutableArray *tableData = [NSMutableArray alloc] init];
NSArray* nameArr = [NSArray arrayWithObjects: #"1", #"2",#"3",nil];
for(int i=0; i<[nameArr count]; i++)
{
[tableData addObject:[jsonArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"watch = %#",[nameArr objectAtIndex:i]]] ];
}
TableView Delegate Methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [tableData count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[tableData objectAtIndex:section ] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell= nil;
//implement your logic to display the cell item;
NSArray sectionData = [tableData objectAtIndex:indexPath.section];
NSArray rowData = [sectionData objectAtIndex:indexPath.row];
return cell;
}
Please note i have not compiled the code. There is a chance of compilation error.
check for section in your cellForRowAtIndexPath method and load data accordingly,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//initialize cell
if (indexPath.section == 0){
// load data
}
return cell;
}
Related
I'm making a title indexed event invitation list. I have an array with EventStatus objects. I'm making the _data array for table like,
for (int i = 0; i < eventStatusList.count; i++) {
NSString *firstName = ((OCEventStatus*)eventStatusList[i]).user.firstName;
[_sortedUsers setObject:((OCEventStatus*)eventStatusList[i]).user.firstName forKey:[firstName substringToIndex:1]];
}
_sortedUserTitles = [[[_sortedUsers allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] mutableCopy];
_data = [eventStatusList mutableCopy];
[self.dataTableView reloadData];
and I think this for loop thing is too slow. Is there a way to do this in a good manner? Following is the title index making up logic with UITablrViewDataSource methods.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [_sortedUserTitles count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSString *sectionTitle = [_sortedUserTitles objectAtIndex:section];
NSArray *sectionUsers = [_sortedUsers objectForKey:sectionTitle];
return [sectionUsers count];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
// return animalSectionTitles;
return _alphabetArray;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [_sortedUserTitles indexOfObject:title];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [_sortedUserTitles objectAtIndex:section];
}
This is error because of in the _sortedUsers dictionary has a string instead of an array. How may I fix this? And also please suggest a fast, good manner to implement this title index.
If you want to create an List of Firstnames try this perhaps.
At interface:
#property(nonatomic, strong)NSMutableDictionary *dict;
#property(nonatomic, strong)NSMutableArray *alphabet;
_dict = [NSMutableDictionary new];
_alphabet = [NSMutableArray new];
[eventStatusList sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
OCEventStatus *ev1 = (OCEventStatus*)obj1;
OCEventStatus *ev2 = (OCEventStatus*)obj2;
return [ev1.firstName compare:ev2.firstName];
}];
for(OCEventStatus *state in eventStatusList){
NSString *firstchar = [[state.firstName substringToIndex:1] lowercaseString];
if([dict objectForKey:firstchar]==nil){
NSMutableArray *tmp = [NSMutableArray new];
[tmp addObject:state];
[_dict setObject:tmp forKey:firstchar];
[_alphabet addObject:firstChar];
}else{
[[dict objectForKey:firstchar] addObject:state];
}
}
Now you have an Array of firstnames in a Dictionary which has the first letter as the key for Example: a -> ["Alfred","Albert",...]
In the Datasource methods you have to return it like this...
-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView{
return dict.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection;(NSInteger)section
{
return [dict objectForKey:[alphabet objectAtIndex:section]].count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
OVEventStatus *state = [[dict objectForKey:[alphabet objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = state.firstName;
return cell;
}
Please try if this fits for you
Update:
If you also want to Sort the Arrays I would recommend to sort the eventListBy firstName first! So you have the correct order when you loop over the eventStatusList.
In this app, I download data from Parse. That data is a list of 5 Messages, each with a category. There are 4 different categories as two of the messages have the same category. I want to put that data into sections on a tableview. Since the data is not ready for sectioning, I had to create 2 mutable arrays that act like index look ups. (followed this guide: https://parse.com/questions/using-pfquerytableviewcontroller-for-uitableview-sections)
Problem: I'm getting this error:
-[__NSArrayM objectAtIndex:]: index 8 beyond bounds [0 .. 4]'
Question is, why am I getting this error and how do I fix it?
I've located the exact line that is the problem. First, here is what you need.
1st Mutable Dictionary:
self.sectionToCategoryMap //This property maps the sections titles to the row indeces for the data. The output it looks like this: (read as, get the 1st category header from object at index 0)
0 = "Section Header 1";
24 = "Section Header 2";
16 = "Section Header 3";
32 = "Section Header 4";
2nd Mutable Dictionary:
self.sections // This maps what items are in what section(category). Output looks like this:
"category 1" =(32);
"category 2" =(24);
"category 3" =(16);
"category 4" =(0,8);
These two Dictionaries are created by this code:
- (void)prepSections:(id)array {
[self.sections removeAllObjects];
[self.sectionToCategoryMap removeAllObjects];
self.sections = [NSMutableDictionary dictionary];
self.sectionToCategoryMap = [NSMutableDictionary dictionary];
NSInteger *section = 0;
NSInteger *rowIndex = 0;
for (MessageItem *messageItem in self.messageList) {
NSString *category = [messageItem valueForKey:#"messageCategory"]; //retrieves category for each message -1st regulator
NSMutableArray *objectsInSection = [self.sections objectForKey:category]; //assigns objectsinsection value of sections for current category
if (!objectsInSection) {
objectsInSection = [NSMutableArray array];
// this is the first time we see this category - increment the section index
//literally it ends up (0:Regulatory)
[self.sectionToCategoryMap setObject:category forKey:[NSNumber numberWithInt:rowIndex]];
section++;
}
[objectsInSection addObject:[NSNumber numberWithInt:(int)rowIndex++]]; //adds message to objects in section
[self.sections setObject:objectsInSection forKey:category]; //adds dict of objects for category
}
}
The error is happening in my in cellForRowAtIndexPath below, specifically the line:
NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row];
(note: categoryForSection is a helper method I defined, its implementation is also below.)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSString *category= [self categoryForSection:indexPath.section];
NSArray *rowIndecesInSection = [self.sections objectForKey:category];
NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row]; //pulling the row indece from array above
//gets to 3 and breaks!!!
messageItem = [self.messageList objectAtIndex:[rowIndex intValue]];
[cell configMessageCell:messageItem indexPath:indexPath];
return cell;
}
For good measure, here is the rest of my code.
- (NSString *) categoryForSection:(NSInteger*)section { //takes section # and returns name of section.
return [self.sectionToCategoryMap objectForKey:[NSNumber numberWithInt:(int)section]];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return (unsigned long)self.sections.allKeys.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *category = [self categoryForSection:section];
NSArray *rowIndecesInSection = [self.sections objectForKey:category];
return [rowIndecesInSection count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *category =[self categoryForSection:section];
return category;
}
Please help me fix this. It has had me stuck for days! Thank you!
Matt
I think the problem lies in declaring these as pointers:
NSInteger *section = 0;
NSInteger *rowIndex = 0;
(Note the weird multiples of 8 in the numbers in your dictionaries - that's because pointer arithmetic works differently from "normal" arithmetic). Try with
NSInteger section = 0;
NSInteger rowIndex = 0;
I have created a UITableView with custom cell & stored name,no,pincode in to these cell.
Here is my Code for array:-
for (int i =0; i<[tempArr count]; i++)
{
NSString *rawData = [tempArr objectAtIndex:i];
if (rawData !=nil)
{
Persons *newPerson = [[Persons alloc]init];
NSArray *data = [rawData componentsSeparatedByString:#"\t"];
newPerson.name = [NSString stringWithFormat:#"%#",[data objectAtIndex:0]];
newPerson.no = [[data objectAtIndex:1] integerValue];
newPerson.pincode = [[data objectAtIndex:2] integerValue];
[allPersons addObject:newPerson];
}
}
Here is my Customcell.h
#interface Customcell : UITableViewCell
#property(weak) Persons* person;
#end
UITableView Datasrouce method:-
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Customcell *cell = [tblStations dequeueReusableCellWithIdentifier:#"personCell"];
if (tableView == self.searchDisplayController.searchResultsTableView)
{
cell.person = filteredContentList[indexPath.row];
[cell.textLabel setText:cell.person.name];
}
else
{
cell.person = allPersons[indexPath.row];
[cell.textLabel setText:cell.person.name];
}
return cell;
}
How do i create Section & index list for all names from A to Z & give title by cell.textLabel.text?
I am following This Tutorial but it has static keys & names added to NSDictionary,NSArray.
In my example i do not know how many names starting with same letter can come in the array. i am also using UISearchDisplayController for search person name.
I want to add number of sections & title for those sections by names that is in the array or cell.textLabel.text dynamically.
i do not know about UISearchDisplayController that these sections & index list will be displaying in UISearchDisplayController so i do not want these sections & index list while searching.
You need to spend a little more time trying to make your questions more clear.
Include a custom implementation of the necessary UITableView data source and delegate methods...
NOTE my assumption that your variable allPersons is an NSMutableArray.
NOTE these do not include for your search results data sets!
Return an NSInteger for the number of sections in your UITableView...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSSet *setData = nil;
NSInteger integerData = 0;
setData = [NSSet setWithArray:allPersons];
integerData = [setData count];
return integerData;
}
UPDATE
Return an NSString for section header titles...
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSOrderedSet *setData = nil;
NSString *stringData = nil;
setData = [NSOrderedSet orderedSetWithArray:allPersons];
stringData = [[setData allObjects] componentsJoinedByString:#" "];
return stringData;
}
...plus others if I have the time...
I am trying to add the image for search (magnifying glass) as the section, to easily go to the search bar.
This is the issue I am experiencing:
My code to setup the sections is as follows:
Note: self.fbFriends is an Array of Dictionaries that include the facebook friends of the user.
self.fbFriends = [[NSArray alloc] initWithArray:[[MESCache sharedCache] facebookFriends]];
self.searchResults = [[NSMutableArray alloc] init];
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
[self.sections setValue:[[NSMutableArray alloc] init] forKey:UITableViewIndexSearch];
// Loop through the friends and create our keys
for (NSDictionary *friend in self.fbFriends)
{
NSString *c = [[friend objectForKey:#"name"] substringToIndex:1];
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 friends into their respective keys
for (NSDictionary *friend in self.fbFriends)
{
[[self.sections objectForKey:[[friend objectForKey:#"name"] substringToIndex:1]] addObject:friend];
}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{
[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]]];
}
Here is my section setup and header views:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return 1;
} else {
return [[self.sections allKeys] count];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return NSLocalizedString(#"FbFriendsSearchControllerSection", #"Fb Friends search controller - section title for search results table view");
} else {
return [[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section];
}
}
Does anyone see how this is incorrect?
This is not a standard practice. There is no need for the extra section just to show a search.
Don't add the "search" section to your self.sections dictionary. Instead, you just have sections for the actual data. Then in your tableView:sectionForSectionIndexTitle:atIndex: method you can do:
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSInteger res = 0;
if (tableView == self.tableView) {
if (index == 0) {
// If the user taps the search icon, scroll the table view to the top
res = -1;
[self.tableView setContentOffset:CGPointMake(0, 0) animated:NO];
} else {
res = ... // return the proper index for your data
}
} else {
res = 0;
}
return res;
}
Side note - you really want to have an array of dictionaries. The main array should represent the sections. As you have it now, you are constantly getting your main dictionary's keys and sorting them. This will be done MANY times - all needlessly. Using an array lets you quickly get the data from the section index.
I started develop an app which is using WCF service with JSON data. I got the data from WCF service but I didn't use it as I want.
here is the JSON data:
{"MenuDoldurAndroidResult":[
{"menu_description":"Turkish Pizza","menu_title":"L Pizza","menu_price":"26 TL"},{"menu_description":"Italiano Pizza","menu_title":"L Pizza","menu_price":"27 TL"},{"menu_description":"Extravaganza","menu_title":"L Pizza","menu_price":"29 TL"},{"menu_description":"Pepporoni Pizza","menu_title":"L Pizza","menu_price":"28 TL"},{"menu_description":"Turkish Pizza","menu_title":"S Pizza","menu_price":"12 TL"},{"menu_description":"Italiano Pizza","menu_title":"S Pizza","menu_price":"13 TL"},{"menu_description":"Extravaganza","menu_title":"S Pizza","menu_price":"15 TL"},{"menu_description":"Pepporoni Pizza","menu_title":"S Pizza","menu_price":"14 TL"}
]}
What I want:
If there are 2 title here, there must be 2 section in table view. Every item must be in their section.
Like this:
-L Pizzas
Turkish Pizza 26 TL
Italiano Pizza 27 TL
Extravaganza Pizza 29 TL
Pepperoni Pizza 28 TL
-S Pizzas
Turkish Pizza 12 TL
Italiano Pizza 13 TL
Extravaganza Pizza 15 TL
Pepperoni Pizza 14 TL
How can I access this item and display like this ?
- (void)viewDidLoad
{
[super viewDidLoad];
//I posted request to service here. I didn't write these parts of code.
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
NSMutableArray *array= [json objectForKey:#"MenuDoldurAndroidResult"];
menu = [[NSMutableArray alloc] initWithCapacity:3];
NSString *descriptionTemp;
NSString *titleTemp;
NSString *priceTemp;
for(int i=0; i< array.count; i++)
{
NSDictionary *menuList= [array objectAtIndex:i];
titleTemp = [menuList objectForKey:#"menu_title"];
descriptionTemp = [menuList objectForKey:#"menu_description"];
priceTemp = [menuList objectForKey:#"menu_price"];
[menu addObject:[NSArray arrayWithObjects:titleTemp,descriptionTemp,priceTemp,nil]];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 2;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;
}
-(NSString *)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section{
if (section==0) {
return #"L Pizzas";
}
else{
return #"S Pizzas";
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell.textLabel.text = [menu objectAtIndex: indexPath.row];
return cell;
}
If your content is static you can try using the answer by Sunny. But if is dynamic it's better to store the data in a different way. Obviously L pizza and S pizza seems to be a category and the rest are like category items.
You need to make a collection of the categories. Demo Project Source Code
- (void)viewDidLoad
{
[super viewDidLoad];
//I posted request to service here. I didn't write these parts of code.
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
NSMutableArray *allPizzas = [json[#"MenuDoldurAndroidResult"] mutableCopy];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"menu_price"
ascending:YES
selector:#selector(compare:)];
[allPizzas sortUsingDescriptors:#[sortDescriptor]];
NSMutableArray *pizzaCategories = [#[]mutableCopy];
//Find unique categories in all the pizzas
NSSet* categories = [NSSet setWithArray: [allPizzas valueForKey:#"menu_title"]];
//Enumerate to form a new reformatted category array
for (NSString *categoryTitle in categories)
{
//Predicate is used to find the items that come under current category
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"menu_title == %#",categoryTitle];
NSArray *categoryItems = [allPizzas filteredArrayUsingPredicate:predicate];
//New dictionary with name of category and category items are formed
NSDictionary *categoryDict = #{#"menu_title":categoryTitle,#"pizzas":categoryItems};
[pizzaCategories addObject:categoryDict];
}
//Assign the new formatted category array to the instance variable for holding categories.
self.categories = pizzaCategories;
}
Modify the datasource of tableView for the new structure
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//The count of categories will give number of sections
NSUInteger sections = [self.categories count];
return sections;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//The number of items in a category is calculated
NSDictionary *category = self.categories[section];
return [category[#"pizzas"] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
//Title for the category
NSDictionary *category = self.categories[section];
return category[#"menu_title"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
NSDictionary *category = self.categories[indexPath.section];
NSArray *categoryItems = category[#"pizzas"];
NSDictionary *categoryItem = categoryItems[indexPath.row];
cell.textLabel.text = categoryItem[#"menu_description"];
cell.detailTextLabel.text = categoryItem[#"menu_price"];
return cell;
}
You can also use the free Sensible TableView framework to fetch the data from the web service and automatically display it in your table view.