Can't access the instance variable - ios

I have an UTableView grouped. I want to split my data into "categories" group. I have an instance variable which contains all groups in my database.
#interface TableViewController : UITableViewController {
// Response from server
NSString* serverResponse;
NSMutableArray *categories; // This NSMutableArray contains all category's server
}
#property (strong) NSString *serverResponse;
#property (strong) NSMutableArray *categories;
I fill my NSMutableArray with the requestFinished method (where all works perfectly)
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *result = request.responseString;
NSArray *jsonArray = (NSArray*)[result JSONValue];
int count = [jsonArray count];
int i;
NSDictionary *jsonDict = [[NSDictionary alloc] init];
for (i = 0; i < count; i++) {
jsonDict = [jsonArray objectAtIndex:i];
NSString *current = [jsonDict objectForKey:#"categoriy"];
[categories addObject:current];
NSLog(#"%d", [categories count]) // Shows 4 -> ok !
}
}
But when I call it in this method :
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"%d", [categories count]) // Show 0 -> bad !
return [categories count];
}
It shows on the consol "0" !
I really dont understand why !
Perhaps someone could help me with this?

The problem might be that numberOfSectionsInTableView: is called before the requestFinished: is called.
I think you have to reload the tableview.
Try putting [self.tableView reloadData], or something like that, at the end of requestFinished:.

Related

UILocalizedIndexedCollation UITableView Indexed List Sort

I'm creating a UITableView with a list of the users contacts. I've successfully imported the list of contacts and am storing them within an array as a dictionary containing each contact's forename, last name & ID reference. I'm trying to implement the UITableView's indexed list function.
I'm hitting problems when trying to partition the array into the different sections in the table's index UILocalizedIndexedCollation. Specifically by the selector. I want to be able to choose whether to sort the names by the forename or surname.
- (void)getContacts
{
// Do any additional setup after loading the view, typically from a nib.
_addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error) {
NSLog(#"GRANTED");
});
NSArray *rawContactData = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(_addressBook);
NSMutableArray *newContactList = [[NSMutableArray alloc] init];
for (int i = 0; i < [rawContactData count]; i++) {
ABRecordRef contact = (__bridge ABRecordRef)rawContactData[i];
NSDictionary *contactData = #{ #"name" : (__bridge_transfer NSString *)ABRecordCopyValue(contact, kABPersonFirstNameProperty),
#"surname" : (__bridge_transfer NSString *)ABRecordCopyValue(contact, kABPersonLastNameProperty),
#"recordID" : [NSNumber numberWithInt:ABRecordGetRecordID(contact)]};
[newContactList addObject:contactData];
}
_tableData = [NSMutableArray arrayWithArray:[self partitionObjects:newContactList collationStringSelector:#selector(name)]];
}
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count]; //section count is take from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
//put each object into a section
for (NSMutableDictionary *object in array) {
NSInteger index = [collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
Selector is sent to the Object which you are sorting in array. In your case your object is type Dictionary which does not implement selector "name" or surname etc. You should create a simple class instead of using dictionary. and just declare properties to your liking
#interface Contact : NSObject
#property (strong,nonatomic) NSString *fname;
#property (strong,nonatomic) NSString *lname;
#property (strong,nonatomic) NSNumber *rerordId;
#end
You can then use this object instead of dictionary you are using.
Contact *contact= [[Contact alloc]init];
contact.fname = (__bridge_transfer NSString *)
ABRecordCopyValue(contact, kABPersonFirstNameProperty);
contact.lname = (__bridge_transfer NSString *)
ABRecordCopyValue(contact, kABPersonLastNameProperty);
contact.recrordId = [NSNumber numberWithInt:ABRecordGetRecordID(contact)].stringValue;
[newContactList addObject:contact];
Then you can use any name in or attribute selector. Just like in your example you will write the function call sorting with fname for example like this
_tableData = [NSMutableArray arrayWithArray:
[self partitionObjects:newContactList
collationStringSelector:#selector(fname)]];

Multi level categories with items on all levels in UITableView

I have to create a UITableView using the JSON response below ( Array ). I have no code for this yet but would love some direction to how i would split this array to accommodate categories and items on all levels.
{
"result":{
"products":[
{
"id":"4",
"product_code":"PR04",
"title":"Product1",
"franchisee_id":"118"
}
],
"categories":[
{
"id":"8",
"name":"Category1"
},
{
"id":"20",
"name":"Category2",
"products":[
{
"id":"9",
"product_code":"PR07",
"title":Product2,
"franchisee_id":"118"
}
]
}
]
}
}
I want to achieve the following result:
items
Category1 > items
Category2 > items
When a category is clicked it would slide to the products in that category. Would really love some direction on this. Some products will not be in categories. Like the example above.
Well....
You need to parse the JSON file. You can easily google for some tutorials but here is a decent one.
Next you are going to need to setup a UITableView to load the items. another good tutorial on UITableViews
Then you are going to need to learn how to pass data between UIViewControllers. Tutorial.
So your steps in the code will be to:
Parse the JSON to separate all the elements.
Setup a UITableView to display the top level elements.
Create a second UITableViewController to push to after a top level item has been selected.
Setup a custom initializer for the second UITableViewController so you can pass it relevant data from the first view controller where you parsed the JSON.
I'm assuming you were looking for a bunch of code on how to do this, but that's no fun :)
Let me know if you run into any troubles and I will be glad to help.
EDIT:
I know I said I wasn't going to dump code but I have some extra time.
Create an NSObject subclass called ProductObject and make the .h look like this:
#import <Foundation/Foundation.h>
#interface ProductObject : NSObject
#property NSString *productCode, *productTitle, *franchiseId, *productId;
#end
Don't do any thing to the .m
Create another NSObject subclass called CategoryObject and make the .h look like this:
#import <Foundation/Foundation.h>
#interface CategoryObject : NSObject
#property NSString *categoryName, *categoryId;
#property NSArray *products;
#end
Again, don't need to do anything to the .m.
Now, in the class that you want to display the UITableView will the Products and Categories (this is all in the .m, the .h is empty):
#import "ViewController.h"
#import "CategoryObject.h"
#import "ProductObject.h"
#interface ViewController ()
//Hooked in from IB
#property (weak, nonatomic) IBOutlet UITableView *table;
//Our UITableView data source
#property NSMutableDictionary *tableObjects;
#end
#implementation ViewController
/**
Parses a the local JSON file
*/
- (void)parseJSON {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"json"];
//création d'un string avec le contenu du JSON
NSString *myJSON = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
NSError *error;
NSDictionary *topLevleJSON = [NSJSONSerialization JSONObjectWithData:[myJSON dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
if (error) {
NSLog(#"Error serializing JSON: %#", error.localizedDescription);
return;
}
NSArray *products = topLevleJSON[#"products"];
NSArray *categories = topLevleJSON[#"categories"];
//Use a NSDictonary so that it contains an NSArray of ProductObjects for the "Products" key, and an array of CategoryObjects for the "Category" key.
self.tableObjects = [NSMutableDictionary new];
//Parse all the products
NSMutableArray *productsInJSON = [NSMutableArray new];
[products enumerateObjectsUsingBlock:^(NSDictionary *productObject, NSUInteger idx, BOOL *stop) {
ProductObject *product = [self createProductObjectFromDictionary:productObject];
[productsInJSON addObject:product];
}];
//Set the array of ProductObjects for the key #"Products"
[self.tableObjects setObject:productsInJSON forKey:#"Products"];
//Parse all the categories
NSMutableArray *categoriesInJSON = [NSMutableArray new];
[categories enumerateObjectsUsingBlock:^(NSDictionary *categoryObject, NSUInteger idx, BOOL *stop) {
CategoryObject *category = [self createCategoryObjectFromDictionary:categoryObject];
[categoriesInJSON addObject:category];
}];
//Set the array of CategoryObjects for key #"Categories"
[self.tableObjects setObject:categoriesInJSON forKey:#"Categories"];
[self.table reloadData];
}
/**
Creates a ProductObject from an NSDictonary.
#param dictionary The dictonary describing the Product parsed from JSON
#return A pretty formatted ProductObject
*/
- (ProductObject*)createProductObjectFromDictionary:(NSDictionary*)dictionary {
ProductObject *product = [ProductObject new];
product.productTitle = dictionary[#"title"];
product.productCode = dictionary[#"product_code"];
product.franchiseId = dictionary[#"franchisee_id"];
product.productId = dictionary[#"id"];
return product;
}
/**
Creates a Category from an NSDictionary
#param dictionary The dictonary describing the Category parsed from JSON
#return A pretty formatted CategoryObject
*/
- (CategoryObject*)createCategoryObjectFromDictionary:(NSDictionary*)dictionary {
CategoryObject *category = [CategoryObject new];
category.categoryId = dictionary[#"id"];
category.categoryName = dictionary[#"name"];
//Check to see if the "products" key exist for the category, if we don't check and just look for it, we will get a crash if it doesn't exist.
if ([[dictionary allKeys] containsObject:#"products"]) {
NSArray *categoryProducts = dictionary[#"products"];
//Parse all the Products for the Category.
NSMutableArray *categoryProductsFormatted = [NSMutableArray new];
[categoryProducts enumerateObjectsUsingBlock:^(NSDictionary *productObject, NSUInteger idx, BOOL *stop) {
ProductObject *product = [self createProductObjectFromDictionary:productObject];
[categoryProductsFormatted addObject:product];
}];
category.products = [NSArray arrayWithArray:categoryProductsFormatted];
}
else {
category.products = nil;
}
return category;
}
#pragma mark -
#pragma mark - UITableView delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.tableObjects allKeys] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//Get the key for this section
NSString *key = [[self.tableObjects allKeys] objectAtIndex:section];
//Return the number of objects for this key.
return [(NSArray*)[self.tableObjects objectForKey:key] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[self.tableObjects allKeys] objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellIdentifier"];
}
//Get all the NSArray associated with this section, which will be an array of ProductObjects or an array of CategoryObjects
NSString *key = [[self.tableObjects allKeys] objectAtIndex:indexPath.section];
NSArray *sectionobjects = (NSArray*)[self.tableObjects objectForKey:key];
id object = [sectionobjects objectAtIndex:indexPath.row];
//Set the cell text based on what kind of object is returned
if ([object isKindOfClass:[ProductObject class]]) {
cell.textLabel.text = [(ProductObject*)object productTitle];
}
else if ([object isKindOfClass:[CategoryObject class]]) {
cell.textLabel.text = [(CategoryObject*)object categoryName];
}
return cell;
}
#pragma mark -
#pragma mark - UITableView delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *key = [[self.tableObjects allKeys] objectAtIndex:indexPath.section];
NSArray *sectionobjects = (NSArray*)[self.tableObjects objectForKey:key];
id object = [sectionobjects objectAtIndex:indexPath.row];
//They selected a product
if ([object isKindOfClass:[ProductObject class]]) {
ProductObject *product = (ProductObject*)object;
NSLog(#"%#", product.productTitle);
NSLog(#"%#", product.productCode);
NSLog(#"%#", product.productId);
}
//They selected a Category
else if ([object isKindOfClass:[CategoryObject class]]) {
//Check to see if the CategoryObject has any ProductObjects associated with it
if ([(CategoryObject*)object products]) {
//Now you will need to pass array of ProductObjects this along to your next view controller.
NSArray *cateogryProducts = [(CategoryObject*)object products];
//For demonstration purposes, i'll run through and print out all the Products for this Category
[cateogryProducts enumerateObjectsUsingBlock:^(ProductObject *product, NSUInteger idx, BOOL *stop) {
NSLog(#"%#", product.productTitle);
NSLog(#"%#", product.productCode);
NSLog(#"%#", product.productId);
}];
}
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//Start parsing the JSON
[self parseJSON];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
EDIT:
If you are wanting to open and close parts of the table like an accordion, take a look at Apple's same code: Table View Animations and Gestures.

My view does not update

My ParseXML method reads the value of NSNumber, which can be incremented by a click of a button.
My ParseXML method has 240 objects, each 8 have an ID from 1 to 30.
The idea is that if i increment the NSNumber from 1 to 2, it refreshes my view and grabs the 8 objects that match the ID and displays it in my view.
That is exactly what is not doing.
.h
#interface FixturesController : UITableViewController
{
NSMutableData *_responseDataFixtures;
int goUp;
NSNumber *test;
}
#property (nonatomic, retain) NSArray *tableDataFixtures;
#property (nonatomic, strong) NSMutableArray *roundParser;
#property (nonatomic, strong) NSString *seasonRoundString;
#property (nonatomic, strong) NSNumber *seasonRoundNumber;
- (IBAction)goUpByOne:(UIButton *)sender;
-(void) parseXMLFixtures:(NSNumber *) giveME;
#end
.m
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self parseXMLFixtures:#2];
}
- (void)viewDidLoad
{
[super viewDidLoad];
goUp = 1;
test = [NSNumber numberWithInt:goUp];
}
// this allows me to increment the count of NSNumber.
- (IBAction)goUpByOne:(UIButton *)sender {
goUp++;
test = [NSNumber numberWithInt:goUp];
goUp = [test intValue];
}
-(void) parseXMLFixtures:(NSNumber *) giveME
{
giveME = test;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"There's no going back"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *xmlString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *xml = [NSDictionary dictionaryWithXMLString:xmlString];
NSMutableArray *items = [xml objectForKey:#"Match"];
NSMutableArray *newFixtureObjectArray = [[NSMutableArray alloc] init];
NSNull *nullValue = [NSNull null];
[newFixtureObjectArray insertObject:nullValue atIndex:0];
[newFixtureObjectArray insertObject:nullValue atIndex:1];
for (NSDictionary *dict in items) {
FixturesObject *myFixtures = [FixturesObject fixtureFromXMLDictionary:dict];
[newFixtureObjectArray addObject:myFixtures];
}
///////
_seasonRoundString = [NSString stringWithFormat:#"%d", [giveME intValue]];
_roundParser = [[NSMutableArray alloc]init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"round == %#", _seasonRoundString];
NSArray *filteredArray = [newFixtureObjectArray filteredArrayUsingPredicate:predicate];
_roundParser = [NSMutableArray arrayWithArray:filteredArray];
[_roundParser insertObject:nullValue atIndex:0];
NSLog(#" Objects of Fixtures in my array %#", _roundParser);
/////
[self setTableDataFixtures:_roundParser];
}
Any suggestions? Thank you. I really need this to work so i can go sleep ˆˆ
Have you impleted the UITableViewDelegate, UITableViewDataSource methods yet?
The methods are:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ }
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{}
You can follow this tutorial

Creating a New Array in each iteration of For Loop

After so long, I'm still trying to create my first iOS app, and I'm facing another problem yet again =(
Basically, I have pulled JSON data from the web, and I get nested arrays. I have no problems iterating all objects in the array (including the parent array) and placing them in a UITableView. The problem is, I am trying to do something dynamic for the table. I am able to create the correct number of sections for the table. To make it easy for you to visualise what I want to do, here is the JSON data:
{"filter_group":[{"filter_group_id":"1","name":"Location","filter":[{"filter_id":"2","name":"Central"},{"filter_id":"8","name":"East"},{"filter_id":"1","name":"North"},{"filter_id":"10","name":"Northeast"},{"filter_id":"9","name":"West"}]},{"filter_group_id":"3","name":"Price","filter":[{"filter_id":"7","name":"Free"},{"filter_id":"5","name":"$0 - $50"},{"filter_id":"6","name":"$50 - $100"},{"filter_id":"11","name":"$100 - $150"},{"filter_id":"12","name":"$150 - $200"},{"filter_id":"13","name":"Above $200"}]}]}
When you copy the messy chunk that I give you into any JSON viewer, you will see that for now, I have 2 parent arrays with some children each. So basically, "Location" and "Price" will go into different sections. I have successfully done that, but I don't know how to put the names of their children in the correct section of the UITableView.
So my idea now is that I am iterating the array, and putting the children names into another array (this results in names of both sections going into 1 array). I was thinking of creating a new array in each iteration:
for(int i = 0; i < [myArray count]; i++) {
//throw children of different parents into different array
NSMutableArray* newArray[i] = (blah blah blah)
}
You will probably have seen the problem now: newArray[i]. Basically, I can live with the fact that I am creating newArray0, newArray1 etc (I need my app to be dynamic). How do I do this in iOS programming?
OR
After reading what I want to do above, and you have a different idea on how I am to put the data in the respective sections, please enlighten me.
Thank you very much!!! I greatly appreciate any help offered =D
P.S. My replies may be slow, so please forgive me
In your application you can use array from json object. I've made sample project to illustrate this aproche:
In header file (ViewController.h):
#import <UIKit/UIKit.h>
#interface ViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) NSMutableArray *data;
#property (nonatomic, strong) NSMutableArray *sectionTitles;
#end
and in ViewController.m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *jsonString = #"{\"filter_group\":[{\"filter_group_id\":\"1\",\"name\":\"Location\",\"filter\":[{\"filter_id\":\"2\",\"name\":\"Central\"},{\"filter_id\":\"8\",\"name\":\"East\"},{\"filter_id\":\"1\",\"name\":\"North\"},{\"filter_id\":\"10\",\"name\":\"Northeast\"},{\"filter_id\":\"9\",\"name\":\"West\"}]},{\"filter_group_id\":\"3\",\"name\":\"Price\",\"filter\":[{\"filter_id\":\"7\",\"name\":\"Free\"},{\"filter_id\":\"5\",\"name\":\"$0 - $50\"},{\"filter_id\":\"6\",\"name\":\"$50 - $100\"},{\"filter_id\":\"11\",\"name\":\"$100 - $150\"},{\"filter_id\":\"12\",\"name\":\"$150 - $200\"},{\"filter_id\":\"13\",\"name\":\"Above $200\"}]}]}" ;
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSArray *filter_groups = [result objectForKey:#"filter_group"];
self.data = [[NSMutableArray alloc] initWithCapacity:[filter_groups count]];
self.sectionTitles = [[NSMutableArray alloc] initWithCapacity:[filter_groups count]];
for (NSDictionary *filter_group in filter_groups) {
NSArray *filters = [filter_group objectForKey:#"filter"];
[self.data addObject:filters];
[self.sectionTitles addObject:[filter_group objectForKey:#"name"]];
}
NSLog(#"%#", self.data);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return self.sectionTitles[section];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [self.data count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSArray *sectionArray = self.data[section];
return [sectionArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"testCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"testCell"];
}
NSArray *sectionArray = self.data[indexPath.section];
NSDictionary *filter = sectionArray[indexPath.row];
cell.textLabel.text = [filter objectForKey:#"name"];
return cell;
}
#end
If you have same question to code, feel free to ask. You can grab code from https://gist.github.com/MaciejGad/8452791
create a new temp array in each loop and add that new temp array to a Mutable array, it is similar to new array[i], while retrieving NSArray *array = [mutablearray objectAtIndex:i]`
please try the following code.
NSMutableDictionary *responseDict; // assign value to responseDict from your response
NSMutableArray *mainArray=[[NSMutableArray alloc] initWithArray:[responseDict objectForKey:#"filter_group"]];
for(int i=0;i<[mainArray count];i++)
{
if([[[mainArray objectAtIndex:i] objectForKey:#"name"] isEqualToString:#"Price"])
{
NSArray *pricechildArray=[[mainArray objectAtIndex:i] objectForKey:#"filter"];
}
else if([[[mainArray objectAtIndex:i] objectForKey:#"name"] isEqualToString:#"Location"])
{
NSArray *locationchildArray=[[mainArray objectAtIndex:i] objectForKey:#"filter"];
}
}
hope this will help you.

UITableView scroll index for array of <FBGraphUser> objects

Really stumped with this need some help! I'm creating a subclass of a UITableViewController to display a list of FB Friends (FBFriendPickerViewController has several limitations for me). I'm able to retrieve an array of id and sort them alphabetically.
However, still can't figure out a way from here to create a separate dictionary to section the FB users into alphabetical sections for indexing.
-(void)captureFacebookFriendUsers
{
//Issue a Facebook Graph API request
NSLog(#"%#", NSStringFromSelector(_cmd));
[FBRequestConnection startForMyFriendsWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error){
if (!error) {
NSLog(#"No error requesting friends");
friendsObjects = [result objectForKey:#"data"]; //Objects are id<FBGraphUser>
friendsNames = [NSMutableArray arrayWithCapacity:friendsObjects.count];
NSMutableArray *friendIds = [NSMutableArray arrayWithCapacity:friendsObjects.count];
//Create a list of friends' Facebook IDs
NSSortDescriptor *firstNameDescriptor = [[NSSortDescriptor alloc]initWithKey:#"first_name" ascending:YES];
friendsObjects = [friendsObjects sortedArrayUsingDescriptors:#[firstNameDescriptor]];
for (NSDictionary *friendObject in friendsObjects) {
[friendIds addObject:[friendObject objectForKey:#"id"]];
[friendsNames addObject:[friendObject objectForKey:#"first_name"]];
}
}
Thanks for taking time to read through this!
Andris's code is excellent, but you don't need to create a separate Person class for this to work. Simply use NSDictionary in place of Person class as follows:
First, create your dictionary - I do this in the View Controller from which I am about to call the table view as part of my button action. You'll also need to declare an NSArray *friends and an NSNumber *friendsCount property in both of your .h files (for your initial view controller and for your table view controller) and synthesize as _friends _friendCount.
- (IBAction)btnAddFriendsTapped:(UIBarButtonItem *)sender {
if (FBSession.activeSession.isOpen){
__block NSArray *friendsArray = [[NSArray alloc]init];
__block NSNumber *friendsArrayCount = [[NSNumber alloc]init];
FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
NSDictionary* result,
NSError *error) {
friendsArray = [result objectForKey:#"data"];
friendsArrayCount = [NSNumber numberWithInt:friendsArray.count];
NSLog(#"Found: %i friends", [friendsArrayCount intValue]);
for (NSDictionary<FBGraphUser>* friend in friendsArray) {
NSLog(#"I have a friend named %# with id %#", friend.name, friend.id);
_friends = [NSArray arrayWithArray:friendsArray];
_friendCount = [NSNumber numberWithInt:[friendsArrayCount intValue]];
}
[self performSegueWithIdentifier:#"friendListSegue" sender:self];
}];
}
Then in the prepareForSegue method pass your dictionary to the Table View Controller not forgetting to import your table view controller header file first.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"friendListSegue"]){
BJFriendListTVC* listOfFriends = segue.destinationViewController;
listOfFriends.friends = _friends;
listOfFriends.friendCount = _friendCount;
}
}
Finally, taking Andris's table code replace the Person class
// Put friends into the appropriate sections
for (NSDictionary<FBGraphUser> *friend in self.friends) {
// Ask the collation which section number the friend name belongs in
NSInteger sectionNumber = [self.collation sectionForObject:friend collationStringSelector:#selector(name)];
// Get the array for that section.
NSMutableArray *sectionFriends = [newSectionsArray objectAtIndex:sectionNumber];
// Add the friend to the section.
[sectionFriends addObject:friend];
}
then when you configure the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSDictionary<FBGraphUser> *person = [[self.sectionsArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
cell.textLabel.text = person.name;
return cell;
}
You'll need to use UILocalizedIndexedCollation for this and call [self.collation sectionForObject:friend collationStringSelector:#selector(name)] to get the index of the section that corresponds to friend name for the locale of the device.
To do that you'll need to store the friend data in a class that has a property "name" (there might be a way to keep using NSDictionary for friend data that I am not aware of).
Here is some code:
// View Controller code
- (void)viewDidLoad
{
[super viewDidLoad];
[self condigureSections];
}
- (void)configureSections
{
// UILocalizedIndexedCollation
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger index, sectionTitlesCount = [[self.collation sectionTitles] count];
// new sections with data
NSMutableArray *newSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
// allocate data array for each of the sections
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[newSectionsArray addObject:array];
}
// Put friends into the appropriate sections
for (Person *friend in self.friends) {
// Ask the collation which section number the friend name belongs in
NSInteger sectionNumber = [self.collation sectionForObject:friend collationStringSelector:#selector(name)];
// Get the array for that section.
NSMutableArray *sectionFriends = [newSectionsArray objectAtIndex:sectionNumber];
// Add the friend to the section.
[sectionFriends addObject:friend];
}
// Now that all the data's in place, each section array needs to be sorted.
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *friendsArrayForSection = [newSectionsArray objectAtIndex:index];
NSArray *sortedFriendsArrayForSection = [self.collation sortedArrayFromArray:friendsArrayForSection collationStringSelector:#selector(name)];
// Replace the existing array with the sorted array.
[newSectionsArray replaceObjectAtIndex:index withObject:sortedFriendsArrayForSection];
}
self.sectionsArray = newSectionsArray;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.sectionsArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[self.collation sectionTitles] objectAtIndex:section];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.collation sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.collation sectionForSectionIndexTitleAtIndex:index];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.sectionsArray objectAtIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
Person *person = [[self.sectionsArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
....
cell.textLabel.text = person.name;
....
return cell;
}
// Store friend data in a class Person so that we could pass the object to
// - (NSInteger)sectionForObject:(id)object collationStringSelector:(SEL)selector
// Example Person.h
#interface Person : NSObject
#property (nonatomic, copy) NSString *id;
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *pictureUrl;
- (id)initWithId:(NSString *)id name:(NSString *)name picture:(NSString *)picUrl;
#end

Resources