I want to display a tableview cell only if my AFNetworking request returns a json object as true. In this example I need place = "Store" to be true in order to display a table view which displays only stores.
the place = "Store" json is returned as part of my location_results array in the following request and I store it with self.place = [dictionary objectForKey:#"place"];
- (void)viewDidLoad
{
[super viewDidLoad];
// LoadLocations
[[LocationApiClient sharedInstance] getPath:#"locations.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id response) {
NSLog(#"Response: %#", response);
NSMutableArray *location_results = [NSMutableArray array];
for (id locationDictionary in response) {
Location *location = [[Location alloc] initWithDictionary:locationDictionary];
[location_results addObject:location];
}
self.location_results = location_results;
[self.tableView reloadData];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error fetching locations!");
NSLog(#"%#", error);
}];
I know I need to start by changing
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.location_results.count;
}
but not sure how.
Then I should be able to add a conditional statement to
- (LocationCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"LocationCell";
but not sure how.
If you have working code for all results, then working code for a subset of the results is simple. Replace references to self.location_results, with [self filteredLocationResults], implemented like this:
- (NSArray *)filteredLocationResults {
return [self.location_results filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"(%K like %#)". #"place" ,#"Store"]];
}
You can just put the "Store" items into the table's data source:
for (id locationDictionary in response) {
Location *location = [[Location alloc] initWithDictionary:locationDictionary];
if([[location objectForKey:#"place"] isEqualToString:#"Store"]) // Add this line
[location_results addObject:location];
}
Related
I'm implementing "swipe to delete" to a TableView with notifications from API. I created a method that deletes a notification when I hard-code its notification id (which is an array). The problem is I can't figure out how to get the exact notification id to delete.
There are TableView Delegate and TableView Data Source methods that somehow get the notification id, so I suppose I should be able to get it for the purpose of my method, but I've run out of ideas.
Here's my API source code:
desc 'delete notifications'
params do
requires :notification_ids, type: Array
end
delete 'notifications/remove', root: :notifications, each_serializer: NotificationSerializer do
require_authentication!
NotificationLogic.delete_notifications params[:notification_ids], current_user
current_user.notifications
end
Here's the method for deleting notifications:
-(void)deleteNotificationWithId:(NSArray*)ids withCompletionHandler:(DeleteNotificationCompletionHandler)handler
{
NSDictionary* params = #{ #"notification_ids" : ids };
__weak typeof(self) weakSelf = self;
ReadNotificationRequest* req = [ReadNotificationRequest new];
req.notificationIds = ids;
[_objectManager deleteObject:nil
path:#"user/notifications/remove"
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
_secondTry = NO;
NSArray* arr = mappingResult.array;
[self notififyAboutNotifications:arr];
handler(YES, arr, nil);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
if (operation.HTTPRequestOperation.response.statusCode == 401 && !_secondTry)
{
[weakSelf relogin:^{
[weakSelf deleteNotificationWithId:ids withCompletionHandler:handler];
}];
return;
}
handler(NO, nil, error);
}];
}
and implementation of the method in NotificationTableView. It works, but I hard-code the array with number:
-(void)setNotifications:(NSMutableArray *)notifications{
_notifications = notifications;
[self reloadData];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//Remove item in array
[self.notifications removeObjectAtIndex:indexPath.row];
// Also remove that row from the table view with an animation
[tableView deleteRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
//Remove hard-coded notification from server
[[Api sharedInstance]deleteNotificationWithId:#[#756]
withCompletionHandler:^(BOOL succes, Message *response, NSError *error) {
if(succes){
} else {
[Utils alert:error.pop_message];
}
}];}
}
#pragma mark TableView Data Source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.notifications.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NotificationTableViewCell* cell = [self dequeueReusableCellWithIdentifier:#"NotificationTableViewCell"];
[cell configureCellWithNotification:self.notifications[indexPath.row]];
return cell;
}
#pragma mark - UITableViewDelegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Notification* not = self.notifications[indexPath.row];
[self.notificationDelegate notificationTapped:not];
}
This code
//Remove item in array
[self.notifications removeObjectAtIndex:indexPath.row];
Deletes the information you need, just before you need it. Instead of deleting it, read the ID out first, then delete it and use the ID.
In my case i have two views in tab bar controller (categories and Home), one with collection view and one with table view.
In the table view, It starts to load data when only user taps(Home) on its tab bar item.I want to load data , when app launch. I am using AFNetworking, and I have called to my data loading method in viewDidLoad. same thing happen with collection view.please help me with this.hope your help thank you. I have attached part of my code, which I used to load data to table view.
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.homeTableView];
// [self homeviewData];
SWRevealViewController *revealController = self.revealViewController;
if (revealController) {
[self.sidebarButton setTarget:self.revealViewController];
[self.sidebarButton setAction:#selector(revealToggle:)];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
// Do any additional setup after loading the view.
}
- (void)viewWillAppear:(BOOL)animated
{
[self homeTableView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)homeviewData
{
homepost = nil;
NSString *mogohomeWebserviceUrl = [NSString stringWithFormat:#"some url"];
AFHTTPRequestOperationManager *homeManager = [AFHTTPRequestOperationManager manager];
[homeManager GET:mogohomeWebserviceUrl parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
homeposts = (NSDictionary *)responseObject;
homepost = [NSMutableArray array];
NSArray *results = [homeposts objectForKey:#"posts"];
for (NSDictionary *all in results)
{
Home *sweetHome = [Home new];
sweetHome.homeImage = [NSString stringWithFormat:#"%#", [all objectForKey:#"thumbnail"]];
sweetHome.homeTitle = [NSString stringWithFormat:#"%#", [all objectForKey:#"title"]];
sweetHome.homewebUrl = [NSString stringWithFormat:#"%#", [all objectForKey:#"url"]];
sweetHome.modifiedTime = [NSString stringWithFormat:#"%#", [all objectForKey:#"modified"]];
[homepost addObject:sweetHome];
[self.homeTableView reloadData];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [homepost count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
HomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"homereuseIdentifier"];
Home *mogoHome = [homepost objectAtIndex:indexPath.row];
NSString *homeimageurlname = [NSString stringWithFormat:#"%#", mogoHome.homeImage];
NSURL *homeimageurl = [NSURL URLWithString:homeimageurlname];
cell.hometitleLabel.text = mogoHome.homeTitle;
[cell.homeimageView setImageWithURL:homeimageurl placeholderImage:nil];
cell.timeLabel.text = [[mogoHome.modifiedTime componentsSeparatedByString:#" "] objectAtIndex:1];
return cell;
}
You got to understand that the data will only be loaded and displayed after the API request to fetch the data is completed...
This implies you should either be ready with data before the screen appears. This is possible only if the data is independent of user action or Master Data, and you can fetch it right at App Launch. If user action is responsible for the fetched data, you have no option but to wait, and show an Activity Indicator. If you decide to do this, you will find a relevant answers how to do that..
All the best...
I am trying to display Facebook JSON from a correctly authenticated account onto a TableView, however no information is displayed despite Json being logged. Here is how I am attempting to display the information onto the TableView:
- (void)fetchFacebookPosts {
[FBRequestConnection startWithGraphPath:#"me/feed" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSLog(#"%#",result);
facebookResponse = [result mutableCopy];
[self.facebookPosts addObjectsFromArray:result[#"data"]];
[self.tableView reloadData];
} else {
NSLog(#"Failure: %#", error);
}
}];
}
#pragma mark - Table view data source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FacebookCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"FacebookCell"];
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Background.png"]];
if (cell == nil) {
cell = [[FacebookCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"FacebookCell"];
}
NSDictionary *entry = facebookPosts[indexPath.row];
// Set Image
// Set User Name
NSString *user = entry[#"story"];
[cell.textLabel setText:user];
return cell;
}
In this code facebookResponse is a NSMutableDictionary
and facebookPosts is a NSMutableArray.
I'm new to iOS development and I am trying to figure out what the best solution to my problem would be. I have a UITableViewController class which calls a method named fetchModules in the viewDidLoad. This fetches all the data I need for my table using AFNetworking 2.
However, my table delegate methods such as numberOfRowsInSectionand cellForRowAtIndexPath are failing because the AFNetworking call has not finished yet and the array I am using to store the data has not been populated.
The actual error I am getting is
Terminating app due to uncaught exception 'NSRangeException'
Here's my code:
#import "HistoryTableViewController.h"
#interface HistoryTableViewController ()
#property NSArray *modules;
#end
#implementation HistoryTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.modules = [[NSArray alloc] init];
[self fetchModules];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)fetchModules
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:self.token forHTTPHeaderField:#"X-Auth-Token"];
[manager GET:#"http://myurl.com/" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(#"JSON: %#", responseObject);
self.modules = [responseObject objectForKey:#"data"];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.modules && self.modules.count) {
return self.modules.count;
} else {
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"Cell Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
NSDictionary *module = [self.modules objectAtIndex:indexPath.row];
cell.textLabel.text = [module objectForKey:#"code"];
return cell;
}
Suggestions?
It's pretty much common solution for async network call.
Add [self.tableView reloadData] inside the AFNetworking success block:
- (void)fetchModules
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:self.token forHTTPHeaderField:#"X-Auth-Token"];
[manager GET:#"http://myurl.com/" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(#"JSON: %#", responseObject);
self.modules = [responseObject objectForKey:#"data"];
[self.tableView reloadData];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}`enter code here`
I think the conditions should be as below.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.modules != nil && self.modules.count > 0) {
return self.modules.count;
} else {
return 0;
}
}
Also do not forget to reload table after you get the response.
Few corrections may help you. First synthesize property "modules". Also in delegate method for creating Table Cell ensure that if "modules" is empty then that method should do no operation on "modules" property and just return "cell". I hope this helps you
I am recieving this error when i scroll to the bottom of my TableView, I dont think its any error with actually retrieving the pictures from the server.:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (15) beyond bounds (15)'
Here is my .m file I cut it to only the actually needed parts of the file:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self entries] count] + tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2 == 0) {
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *created = [tweet objectForKey:#"created_at"];
NSLog(#"%#", created);
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}else {
static NSString *CellIdentifier = #"InstagramCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *entry = [self entries][indexPath.row];
NSString *imageUrlString = entry[#"images"][#"low_resolution"][#"url"];
NSURL *url = [NSURL URLWithString:imageUrlString];
[cell.imageView setImageWithURL:url];
return cell;
}
}
- (void)fetchTweets {
self.twitterClient = [[AFOAuth1Client alloc] initWithBaseURL:[NSURL URLWithString:#"https://api.twitter.com/1.1/"] key:#"TWEETER_KEY" secret:#"TWEETER_SECRET"];
[self.twitterClient authorizeUsingOAuthWithRequestTokenPath:#"/oauth/request_token" userAuthorizationPath:#"/oauth/authorize" callbackURL:[NSURL URLWithString:#"floadt://success"] accessTokenPath:#"/oauth/access_token" accessMethod:#"POST" scope:nil success:^(AFOAuth1Token *accessToken, id responseObject) {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"statuses/home_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
tweets = responseArray;
[self.tableView reloadData];
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
There needs to be tight coordination between the return value from numberOfRowsInSection and the array access that the code does in cellForRowAtIndexPath.
Consider this, your entries array and tweets array each have 4 elements. So numberOfRowsInSection returns 8. The cellForRowAtIndexPath method gets called to configure row 6. Your code will do this: NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
But wait... that array has only 4 elements, right? Asking for something at index 6 will generate the crash you see.
It might be simpler to write a methods to interleave the arrays into a single array, then answer the count of the combined array in numberOfRowsInSection. In cellForRowAtIndexPath, the array elements themselves should be able to tell you what kind of row you have (not the index). Dereference the combined array and configure the table accordingly.
EDIT - I'll try to make my advice more explicit in code: Let's say, for simplicity, that "entries" and "tweets" are both arrays of NSDictionaries and that your app wants to organize them in the UI entries first, then tweets.
// in interface:
#property (nonatomic, strong) NSArray *myModel;
// in code:
- (NSArray *)myModel {
if (!_myModel) {
NSMutableArray *array = [NSMutableArray arrayWithArray:[self entries]];
[array addObjectsFromArray:tweets];
_myModel = [NSArray arrayWithArray:array];
}
return _myModel;
}
We call this 'myModel' for a reason. It's the datasource of the table. The datasource protocol is asking explicitly about this array (and no other).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.myModel.count;
}
Now cellForRowAtIndexPath is going to ask you to configure that many (myModel count) rows, numbered 0..count-1. You must dereference the same array -- myModel -- for all datasource methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *myModelForThisRow = self.myModel[indexPath.row];
// get the cell = deque...
cell.textLabel.text = myModelForThisRow[#"someKey"];
return cell;
}
What if your tweets or entries array changes? No problem, just rebuild the model like this:
- (IBAction)tweetsOrEntriesDidChange:(id)sender {
self.myModel = nil; // the "lazy" getter will rebuild it
[self.tableView reloadData]; // this will call the datasource which will call the lazy getter
}
You are trying to go read into an array outside of it's bounds.
That array access look very suspicious
if (indexPath.row % 2 == 0) {
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
as well as this one
NSDictionary *entry = [self entries][indexPath.row];
From what I've seen your array tweets and [self entries] don't contain as many object each as there is row in your table section.
I take my assomption from here :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self entries] count] + tweets.count;
}
NSRangeException is thrown because you are trying to access an index which is not within the valid range for your array. Try setting an "Exception breakpoint" in Xcode to see where it's coming from. Check here to know more about Exception breakpoints
This is typically caused by an off by one error.