Array of image url to ui collectionview - objective-c

I am doing a json query to get an array of image urls. I want to display the images on a uicollection view. But I am missing something. I think I have to parse thru the array and set nsurl to each item. Then put each item in nsdata. But I am not sure. I don't want to use 3rd party software either.
Here is a sample of my code.
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
coProFeedURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
}
///new return all company data array
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray * getProductsArray = [json objectForKey:#"CompanyProduct"]; //2 get all product info
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"companyID = %#", passCoData];//added create filter to only selected company
NSArray * filteredProductArray = [getProductsArray filteredArrayUsingPredicate:predicate];//only products for selected company
///doing parsing here to get array of image urls
finalImageArray = [filteredProductArray allObjects];//original
NSLog(#" url images :%#", finalImageArray);
//NEED TO GET FINALIMAGEARRAY TO NSURL TYPE AND SET TO IMAGE?
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return finalImageArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";//test form file works
ItemCollectionViewCell *cell = (ItemCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];//test form file works
///then add the finalImageArray?
return cell;
}
Here the output of my finalImageArray that i want to apply to uicollectionview, here is the log data:
{(
"http://www.test/inventory/images/bball.jpg",
"http://www.test/images/bird%20tank_0.jpg"
)}
So am i missing something like nsurl, or nsdata, or what. How do i get this array of image url to display on a uicollectionview? Thanks for the help!

Just reload Collection view with updated data of array. As you requested on background thread. Now, you need to reload data on main thread.
- (void)fetchedData:(NSData *)responseData {
//YOUR CODE ...
finalImageArray = [filteredProductArray allObjects];//original
NSLog(#" url images :%#", finalImageArray);
//NEED TO GET FINALIMAGEARRAY TO NSURL TYPE AND SET TO IMAGE?
dispatch_async(dispatch_get_main_queue(), ^{
[collectionView reloadData];
});
}
Hope, it'll help you.

Related

Populate UICollectionView Images From Document Directory AND Update Custom NSMutableArray object?

In my data controller I have book objects and each object contains these properties (among others):
novel.title = #"Book One";
novel.imageArray = [[NSMutableArray alloc] initWithObjects: [UIImage imageNamed: #"book1image1"], nil];
In the app, users can add a book image using UIImagePickerController like this:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(nonnull NSDictionary<NSString *,id> *)info {
[picker dismissViewControllerAnimated:YES completion:nil];
_addedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
_addedImage = [self scaleImage:_addedImage toSize:CGSizeMake(120, 168)];
[_book.imageArray addObject:_addedImage];
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:_book.imageArrayID];//folder name
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath])
[[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:&error];
int i = 0;
for(_addedImage in _book.imageArray)
{
_book.bookAddedToArray = YES;
NSString *fileName = [stringPath stringByAppendingFormat:#"/image%i.jpg", i++];//image name
NSData *data = UIImageJPEGRepresentation(_addedImage, 1.0);
[data writeToFile:fileName atomically:YES];
}
[self.collectionView reloadData];
}
This works perfectly fine. All images are saved in the right place as expected.
As long as the app stays open, the new images can be viewed in the collectionView. You can navigate anywhere in the app, come back, and view the collectionView some more. When the app is completely exited out of and then reopened, the collectionView is reset and only shows the initial image that was set in the dataController (no matter what code I've implemented so far. It just always resets). All the user generated images are still in their respective folders in the documents directory but I cannot seem to update the cellForItemAtIndexPath: with the stored images presumably because the images are not being saved to the _book.imageArray. Right now I have the cell being populated by the default imagearray so, of course, that is what will show up. How do I update the array and pull from documents directory to show user images?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if(_book.bookAddedToArray)
{
//*** HOW TO UPDATE _BOOK.IMAGEARRAY ????
cell.bookImageView.image = [_book.imageArray objectAtIndex:indexPath.row];
}
else
{
cell.bookImageView.image = [_book.imageArray objectAtIndex:indexPath.row];
}
return cell;
}
I've read that you can store an array of directory paths in nsuserdefaults and retrieve it to populate an imagearray but I have yet to find a solution that works for me. None of the answers seem to address loading into a collectionview or tableview. Can anyone point me in the right direction? Please? Anyone? Lol. Let me know if you need to see more code. All suggestions are much appreciated!
Eventually the app will need to be rewritten as it has grown beyond the scope of my original code, but at this time, this is what I'm working with. I'll probably eventually use CoreData but I've even seen where people on S.O. recommended against storing images in CoreData and to use the documents directory. That still leaves me in this same situation then.
Yes! I got it figured out and it works like a charm. It seems that I could not actually update my custom NSMutableArray. I could make it read another array but could not actually change the array itself. (I'm referring to the _book.imageArray.)
Everything stayed the same in the didFinishPickingMediaWithInfo.
I didn't need to add anything to the cellForItemAtIndexPath. It is just simply:
if(_book.bookAddedToArray)
{
cell.bookImageView.image = [_book.imageArray objectAtIndex: indexPath.row];
}
I created a property for my new array:
#property (nonatomic, strong) NSMutableArray *imagePathArray;
All the magic happens in ViewDidLoad.
- (void)viewDidLoad
{
[super viewDidLoad];
//initialize new array here
_imagePathArray = [[NSMutableArray alloc] init];
if (_book.bookAddedToArray)
{
int i = 0;
//this is the path to the folder.
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:_book.imageArrayID];
int z;
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stringPath error:NULL];
for (z = 0; z < (int)[directoryContent count]; z++)
{
//this is the path to each image file
NSString *fileName = [stringPath stringByAppendingFormat:#"/image%i.jpg", i++];
NSLog(#"imagefile = %#", fileName);
UIImage *fileNameImage = [UIImage imageWithContentsOfFile:fileName];
//add each image to a new array
[_imagePathArray addObject:fileNameImage];
}
//set my array to the new array
_book.imageArray = _imagePathArray;
}
}
I can thank this post for helping to read the images within each folder:
go to link.
//these are all properties in my Data Class.
_book.imageArrayID
_book.bookAddedToArray
_book.imageArray
I hope this helps someone out!

Load JSON data into table view using Xcode 4.6

I'm trying to load a data from a JSON file. I've added this file to my Project Files in Xcode 4.6, but every time I run my code, I get empty cells in the TableView, with no data at all, even though I've declared a properly for each label and assigned a value for them from the JSON file.
I have created two classes: events.h and .m, jsonloader.h and .m, and imported them into my main view controller.
Here's a sample of my JSON data:
{
"Events": [
{
"id": "1",
"title": "parr jazz presents zoe chiotis",
"performingArtists": "Zoe Chiotis",
"day": "Tuesday",
"date": "2013-12-03",
"lastUpdated": "2013-12-03",
"startingTime": "8:00:00 PM",
"endingTime": "1:00:00 AM",
"venue": "STUDIO2",
"location": {
"streetAddress": "33-45 Parr Street",
"postCode": "L1 4JN"
},
"music genre": "Jazz",
"information": "ZOE CHIOTIS is a singer/songwriter/guitarist based in Manchester. ",
"image": "http://bandonthewall.org/images/content/large/zoe-chiotis-1764.jpg",
"thumb": "http://swingbands.co.uk/wp-content/zoechiotis.jpg"
},
...
]}
This is event.m:
#implementation JSONLoader
- (NSArray *)eventsFromJSONFile:(NSURL *)url {
// Create a NSURLRequest with the given URL
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:30.0];
// Get the data
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
// Now create a NSDictionary from the JSON data
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// Create a new array to hold the locations
NSMutableArray *events = [[NSMutableArray alloc] init];
// Get an array of dictionaries with the key "Events"
NSArray *array = [jsonDictionary objectForKey:#"Events"];
// Iterate through the array of dictionaries
for(NSDictionary *dict in array) {
// Create a new event object for each one and initialise it with information in the dictionary
Events *dataevents = [[Events alloc] initWithJSONDictionary:dict];
// Add the Location object to the array
[events addObject:dataevents];
}
// Return the array of Location objects
return events;
}
and in my mainViewController.m I included this:
#implementation MV_HomeViewController {
NSArray *_events;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// Create a new JSONLoader with a local file URL
JSONLoader *jsonLoader = [[JSONLoader alloc] init];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"EventsData" withExtension:#"json"];
// Load the data on a background queue...
// As we are using a local file it's not really necessary, but if we were connecting to an online URL then we'd need it
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_events = [jsonLoader eventsFromJSONFile:url];
// Now that we have the data, reload the table data on the main UI thread
[self.myTableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
});
}
#pragma mark - Table view data source
- (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 [_events count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"eventCell";
MV_CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil) {
cell = [[MV_CustomCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
//[[cell textLabel] setText:_eventType];
Events *event = [_events objectAtIndex:indexPath.row];
cell.TitleLabel.text = event.title;
cell.GenreLabel.text = event.musicGenre;
return cell;
}
Does anybody know what the problem is here?

How to display the json's Mutable Array data like Expandable/Collapsible TableView in iphone?

I am new to IOS. I am getting the data from WebService and stored that data into MutableArrays using json serialization. But now i have to display that data in TableView like Expandable and Collapsible Cells. Please send me the sample code to solve this problem.
NSMutableArray *arr=[NSMutableArray alloc]init];
NSString *Str_Menu_desc=[NSString alloc]init];
NSURL *url=[NSURL URLWithString:#"http://www.google.com/CategoryInfo.php"];
NSData *data=[NSData dataWithContentsOfURL:url];
NSError *err;
json=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&err];
NSEnumerator *enu=[json objectEnumerator];
while (dic =(NSDictionary *)[enu nextObject])
{
[arr addObject:[dic1 objectForKey:#"menu_name"]];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return [arr count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
cell=[[UITableViewCell alloc]init];
cell.textLabel.text=[arr objectAtIndex:indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *local=[arr objectAtIndex:indexPath.row];
NSURL *url=[NSURL URLWithString:#"http://www.google.com/SubCategoryInfo.php"];
NSData *data=[NSData dataWithContentsOfURL:url];
NSError *err;
json=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&err];
NSEnumerator *enu=[json objectEnumerator];
while (dic =(NSDictionary *)[enu nextObject])
{
if (local isEqual:[dic objectForKey:#"category_name"]])
{
Str_Menu_desc=[NSString stringWithFormat:#"%#",[dic objectForKey:#"Sub_Category_name"]];
}
}
}
Now, i got SubCategory Names using selected Category Name. I have to display Category and Subcategory Names in Expandable/Collapsible TableView. Thanks in Advance.
What do you mean by expandable-collapsible cells? May be it something like this: Expanding/Collapsing TableView Sections?
In another way data, received in -tableView:tableView didSelectRowAtIndexPath: method, put in new UITableViewController and push it controller in views hierarchy with line of code like this: [self.navigationController pushViewController:content animated:YES]; Remember: navigationController must be created first, otherwise code will be non-working.

How to prevent textfields from getting empty when adding/removing UITableView rows?

I'm creating a UITableView in which product information can be added. In each row, the user can add information about a product, and, obviously, the user can set the number of rows himself.
the user can add or remove one row a time by tapping either the "add row" or "remove row" button in the NavigationBar. this is how it works:
- (void)viewDidLoad
{
[super viewDidLoad];
tableRows = [NSNumber numberWithInt:12];
}
-(void) addRow
{
NSNumber *addRow =[NSNumber numberWithInt:1];
tableRows= [NSNumber numberWithInt:(tableRows.intValue + addRow.intValue)];
[self.tableView reloadData];
NSLog(#"%#", tableRows);
}
-(void) removeRow
{
NSNumber *addRow =[NSNumber numberWithInt:1];
tableRows= [NSNumber numberWithInt:(tableRows.intValue - addRow.intValue)];
[self.tableView reloadData];
NSLog(#"%#", tableRows);
}
- (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 tableRows.intValue;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CustomCellIdentifier = #"CustomCellIdentifier ";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[CustomCell class]])
cell = (CustomCell *)oneObject;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSUInteger *row = [indexPath row];
return cell;
}
The editing works perfect but when I add or remove a row, the text I inserted in the textfields of my tableview disappears.
does anybody know how to prevent this?
A couple things: The table view doesn't have responsibility to remember what's in each of the cells. It throws away cells as the scroll away and asks the datasource to initialize them again via cellForRowAtIndexPath. Reloaddata - which you use in your add/remove methods - will cause the table to refresh all of the visible cells. Don't expect anything to appear in your table that isn't setup in cellForRowAtIndexPath.
Next, your "model" for this table is an NSNumber "tableRows" indicating the number of rows. This is an insufficient model for a table view. Replace it with an NSMutableArray. At the very least, this array should contain strings representing the state of each text field. (and it might need even more elaborate objects, but start with strings).
With that, your view controller class will look more like this...
// this is your table's model
#property (nonatomic, strong) NSMutableArray *rows;
// in init for the class
_rows = [NSMutableArray array];
// somewhere else, put some data in it
[self.rows addObject:#"Foo"];
[self.rows addObject:#"Bar"];
Now your datasource methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.rows.count;
}
Then, in cellForRowAtIndexPath:
NSUInteger *row = [indexPath row]; // as you have it
NSString *rowText = self.rows[row]; // this is new syntax, the old way is [self.rows objectAtIndex:row];
// your CustomCell needs to provide a way to get at the textField it contains
// it might do this with an outlet or viewWithTag...
cell.myTextField.text = rowText;
return cell;
Finally, text fields in the cells pose a particular challenge. How to save their current state when the view isn't scrolling. This problem has been asked and answered multiply in SO (here, for example). In a nutshell, the most common solution is to make the view controller the delegate of the text fields in the cells. Then, on textFieldDidEndEditing, save the value of the textField in your model like this...
- (void)textFieldDidEndEditing:(UITextField *)textField {
NSIndexPath *indexPath = [self indexPathOfCellWithSubview:textField];
self.rows[indexPath.row] = textField.text;
}
// I think this is the best way to get the index path of a cell, given some subview it contains
- (NSIndexPath *)indexPathOfCellWithSubview:(UIView *)view {
while (view && ![view isKindOfClass:[UITableViewCell self]]) {
view = view.superview;
}
return [self.tableView indexPathForCell:(UITableViewCell *)view];
}
EDIT Say there's more to the model than just a single string. This is where you would apply a custom subclass of NSObject.
// MyModel.h
#interface MyModel : NSObject
#property (strong, nonatomic) NSString *itemName;
#property (assign, nonatomic) CGFloat price;
#property (strong, nonatomic) NSString *imageFileName;
#property (strong, nonatomic) UIImage *image;
- (id)initWithItemName:(NSString *)itemName price:(CGFloat)price imageFileName:(NSString *)imageFileName;
- (NSString *)stringPrice;
- (void)setStringPrice:(NSString *)stringPrice;
#end
// MyModel.m
#implementation MyModel
- (id)initWithItemName:(NSString *)itemName price:(CGFloat)price imageFileName:(NSString *)imageFileName {
self = [self init];
if (self) {
_itemName = itemName;
_price = price;
_imageFileName = imageFileName;
}
return self;
}
// override the image getter to "lazily" create and cache the image
// if the images are on the web, this will require a slighly more elaborate method
// employing NSURLConnection.
- (UIImage *)image {
if (!_image) {
_image = [UIImage imageNamed:self.imageFileName];
}
return _image;
}
// added these to show you how you can conveniently encapsulate other
// behavior, like type conversion or validation, though, real ones of these
// would probably use NSNumberFormatter
- (NSString *)stringPrice {
return [NSString stringWithFormat: #"%.2f", self.price];
}
- (void)setStringPrice:(NSString *)stringPrice {
self.price = [stringPrice floatValue];
}
Now you can create one like this and add it to your table. (Be sure to #import "MyModel.h")
[self.rows addObject:[[MyModel alloc] initWithItemName:#"Toaster" price:39.95 imageFileName:#"toaster.png"]];
The view controller containing the table stays more or less the same (when you change one class a lot and change a closely related class very little, it tells you that your OO design is probably pretty good). For the fancy model replacing the string, we need to change cellForRowAtIndexPath...
NSUInteger *row = [indexPath row];
MyModel *myModel = self.rows[row];
cell.itemNameTextField.text = myModel.itemName;
cell.priceTextField.text = [myModel stringPrice];
cell.imageView.image = myModel.image;
// additional OO idea: teach your cell how to configure itself and move the foregoing code there
// [cell configureWithModel:myModel];
return cell;
ANOTHER EDIT: We can teach this model how to post itself to a remote web service as follows:
- (void)post {
NSString *hostStr = #"http://myhost/create_product.php";
NSURL *url = [NSURL URLWithString:hostStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"POST";
NSString *post =[NSString stringWithFormat:#"item_name=%#&price=%#",self.itemName, [self stringPrice];
NSString *postEscaped = [post stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *postData = [postEscaped dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
[request setHTTPBody:postData];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"response %#", string);
} else {
NSLog(#"error %#", error);
}
}];
}
Declare this method in the .h, add other fields to the post as you see fit (e.g. the image file name, etc.)
In your view controller, pick out the action that means the user wants to commit the new row (maybe it's when the text field is finished editing?), and add this...
// text field finished editing
MyModel *myModel = self.rows[indexPath.row];
myModel.itemName = textField.text;
[myModel post];
Since the image will probably come from your remote service, you'll want to change the lazy loading image getter I added earlier. The right way to load this image is asynchronously, but doing so complicates the interaction with the table view too much to discuss here. Refer to apple docs or this SO post to learn more about that. In the meantime, here's the quick -- but basically wrong -- way to get the image synchronously...
- (UIImage *)image {
if (!_image) {
// note - now the file name must be of the form #"http://host/path/filename.png"
NSURL *url = [NSURL URLWithString:self.imageFileName
_image = [UIImage imageWithContentsOfURL:url];
}
return _image;
}
It would be helpful to see your code for cellForRowAtIndexPath, we need to know more about the model you intend to store data in.
When you delete a row from the table, that cell is thrown out, and the tableview will not remember the contents automatically. You must save the changes in a model object as they occur, and then use that to populate the cell's contents when returning a cell from cellForRowAtIndexPath.

Populate UITableView in ViewController class from separate DataController class that is using grand central dispatch

I have a UITableView in a ViewController class. The ViewController class uses a custom dataController (specified in the AppDelegate). In the dataController class I'm fetching some JSON from the web, parsing it to an NSMutableArray, then using that data to populate the UITableView in the ViewController.
This all works great, except there is a noticeable lag when the app starts up since it takes time to get the JSON and work with it. I'd like to show an empty UITableView with an activity indicator while this data is loading. Unfortunately whenever I put the code in the dataController class into a dispatch queue, the UITableView is never populated with data (the data is loaded according to the log). All I see is a blank table.
I guess my main issue is I don't know how to set up a queue in the dataController class and then update the UI with the data in that queue but in another class.
Relevant code:
from dataController class:
- (void)initializeDefaultDataList {
NSMutableArray *dataList = [[NSMutableArray alloc] init];
self.masterDataList = dataList;
dispatch_queue_t myQueue = dispatch_queue_create("name.queue.my", NULL);
dispatch_async(myQueue, ^{
NSString *jsonString = [JSONHelper JSONpostString:#"http://webservice/getData"];
NSError *jsonError = nil;
//convert string to dictionary using NSJSONSerialization
NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData: [jsonString dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &jsonError];
if (jsonError) NSLog(#"[%# %#] JSON error: %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), jsonError.localizedDescription);
NSArray *dataArray = [jsonResults objectForKey:#"d"];
for (NSString *dataItem in dataArray) {
[self addDataWithItem:dataItem];
}
});
}
from AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
MyMasterViewController *firstViewController = (MyMasterViewController *)[[navigationController viewControllers] objectAtIndex:0];
MyDataController *aDataController = [[MyDataController alloc] init];
firstViewController.dataController = aDataController;
return YES;
}
from ViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//would this go here?
dispatch_async(dispatch_get_main_queue(), ^{
MyObject *objectAtIndex = [self.dataController objectInListAtIndex:indexPath.row];
[[cell textLabel] setText:objectAtIndex.name];
});
return cell;
}
In case you couldn't tell I'm really new to iOS and Objective C. Any help or hints you can give would be greatly appreciated. I'm not even sure if I'm expressing my question properly - it just seems that what I want to do shouldn't be this difficult. Thanks!
EDIT
Ok, so maybe this is a life cycle issue. Just realized that anything I set within the async block is nil outside the block, at least it is until it's too late to make a difference. That's why cellForRowAtIndexPath is never called - because the masterDataList being passed to the UITableView is empty. Tested this by initializing
__block NSString *s = [[NSString alloc] init];
outside the block, then setting a value inside the block:
s = #"Testing...";
and finally NSLogging the value of s after the block has supposedly run. But obviously the block hadn't run yet because s was nil.
It looks like you're doing the right thing to get back on the main thread after your work is done, but you haven't told the table view it needs to show the new data. [self.tableView reloadData] ought to help.
As I discovered in posts such as this one, data set within the async dispatch cannot be used outside the queue. As I understand it, the whole idea of GCD is that it determines when it's best to run and dispose of data.
As a result, I ended up splitting up my code so I was only using the DataController class to, well, control data (I know, revolutionary) and moved all the GCD parts to my ViewController. Amended code:
DataController class:
- (void)initializeDefaultDataList {
NSMutableArray *dataList = [[NSMutableArray alloc] init];
self.masterDataList = dataList;
}
ViewController class:
#interface ObjectMasterViewController () {
__block NSString *jsonString;
}
#end
...
- (void)getJSONString
{
jsonString = [JSONHelper JSONpostString:#"http://webservice/getData"];
}
...
- (void)initData {
NSError *jsonError = nil;
//convert string to dictionary using NSJSONSerialization
NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData: [jsonString dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &jsonError];
if (jsonError) NSLog(#"[%# %#] JSON error: %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), jsonError.localizedDescription);
NSArray *dataArray = [jsonResults objectForKey:#"d"];
//loop through array and add items to list
for (NSString *dataItem in dataArray) {
[self addDataWithItem:dataItem];
}
}
...
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t myQueue = dispatch_queue_create("name.queue.my", NULL);
dispatch_async(myQueue, ^{
//initalize service url string
[self getJSONString];
dispatch_async(dispatch_get_main_queue(), ^{
//retrieve data
[self initData];
//reload tableView with new data
[self.tableView reloadData];
});
});
}
Hope this can help someone who might be in the same boat I was in.