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.
Related
I have three tableviews inside of one View Controller (their visibility is controlled by a segment control). That said, I only want cells to have the option of being deleted from self.friendsView, and not the other tableviews. I have the following code below in my View Controller, but the ability to swipe and delete a cell is visible on all three of my tableviews, not just self.friendsView. How can I fix this?
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.friendsView) {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
NSMutableDictionary *nodeData = [[self.myFriendData objectAtIndex:indexPath.row] mutableCopy];
NSString *nid = [nodeData objectForKey:#"nid"];
[nodeData setObject:nid forKey:#"nid"];
NSLog(#"%#",nid);
[self.myFriendData removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[DIOSNode nodeDelete:nodeData success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"node deleted!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"could not delete node!");
}];
}
}
}
In addition to implementing commitEditingStyle you also need to implement the editingStyleForRowAtIndexPath delegate method.
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.friendsView) {
return UITableViewCellEditingStyleDelete;
} else {
return UITableViewCellEditingStyleNone;
}
}
I am trying to delete a row in a UITableView (PFQueryTableViewController). The object deletes in the class, but is only reflected in the table when I refresh the table. Here is the code I am using.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[tableView reloadData];
}];
}
}
Sussed it.
Instead of [tableView reloadData], I've used [self loadObjects].
However, the usual delete animation is not there.
You have to make all UI updates in main thread.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[tableView deleteRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationFade];
}];
}
}
Any insight would be greatly appreciated.
I have an app with a shared photo stream and I need the user to only be able to swipe and delete their own photo items...my basic code setup works to delete with swipe, but I can't figure out the best way to code so that it verifies first that the PFUser matches as the owner of the PFOject before allowing for swipe and delete
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
}];
}
Grazie,
As per your code you have to manually refresh the table because you have not refreshed the table. Add the code as follows
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Remove the row from data model
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// Add refresh control & reload the tableView.
[self refreshControl];
[tableView reloadData];
}];
}
This worked for me
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[self loadObjects];
}
}];
}
}
First off ,in your photo class you need to have a pointer to the user who owns the photo.
Now in your table view controller you have an array of your PFObjects that you called objects.
So to allow the user to delete only his/her photos you could do something like this.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
PFObject* photoObject=self.objects[indexPath.row];
PFUser* owner=photoObject[#"owner"];
[owner fetchIfNeeded];
if ([owner.objectId isEqualToString:[PFUser currentUser].objectId]) {
return YES;
}
else return NO;
}
However a better way to do this is to have a custom class and make your table view controller model that class as the following:
#interface MyPhoto : NSObject <NSCoding>
#property UIImage* photo;
#property PFObject* parseObject;
#property BOOL iOwnThisPhoto;
//Class method to fetch the photo stream from parse and return an array of MyPhoto*
+(NSArray*)fetchStream;
#end
In my table view controller I would have something like this
#interface PhotoStreamVC ()
//Private property
#property NSMutableArray* photos;
#end
#implementation PhotoStreamVC
- (void)viewDidLoad
{
[super viewDidLoad];
//Do background fetch
[self performSelectorInBackground:#selector(fetchPhotoStream) withObject:nil];
}
-(void)fetchPhotoStream
{
self.photos=[MyPhoto fetchStream];
//Reload table view from the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
MyPhoto* photo=self.photoStream[indexPath.row];
return photo.iOwnThisPhoto;
}
#end
After deleting a row, it is still showed.
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
Audio *current = [logic findAudioByRow:indexPath.row];
[logic deleteAudio:current callback:^{
dispatch_async(dispatch_get_main_queue(), ^{
//some audio player logic that does not important for the topic
});
}];
}
this method calls that function:
-(void)deleteAudio:(Audio *)audio callback:(voidCallback)callback
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSString * ownerId = [NSString stringWithFormat:#"%ld", [audio.owner_id integerValue]];
NSString * audioId = [NSString stringWithFormat:#"%ld", [audio.aid integerValue]];
APIData *apiData = [[APIData alloc] initWithMethod:DELETE_AUDIO_BY_ID user:[[UserLogic instance] currentUser] queue:requestQueue params:[[NSMutableDictionary alloc] initWithObjectsAndKeys:audioId,#"aid", ownerId, #"oid", nil]];
[APIRequest executeRequestWithData:apiData block:^(NSURLResponse *response, NSData *data, NSError *error) {
}];
callback();
[self updateContent:YES];
});
}
I don't understand when I should call reloadData to update view.
Can't see the delete method in your code. This is how you have to use the delete row methods.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
[mySourceArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
When you delete a row of table view also remove the related record from array, then after reload tableview.
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];
}