Deleting a row in a UITableViewController with an alert confirmation - objective-c

I have an UITableViewController, set up so that users can delete rows. This is my code for the handler of the delete action, and the delegate method for the UIAlertView:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"t" message:#"del?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Delete", nil];
[alert show];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
if (buttonIndex != [alertView cancelButtonIndex]) {
//my code to delete from the data source is here. it works fine.
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationFade];
}
}
The problem is in that last line where it's supposed to actually delete the rows from the tableView. If I put that (and the code that deletes from the data source) in the first method instead of the UIAlertView stuff, it works fine.
What is the proper way of doing this?

The problem was that the UIIndexPath object was wrong inside that method! I guess it's because there is no row selected while the alert view is showing. So, I saved it before doing the UIAlertView code into a property of the view controller class, and retrieved it later in the second method, and it works fine.

Related

Two Alertviews on one view [duplicate]

This question already has answers here:
Two Alert Views in One View Controller - buttonIndex Response
(4 answers)
Closed 7 years ago.
EDIT:
Problem Solved ==> Simply giving a tag solved the problem.
I have the following problem:
On a view I have two UIalertviews:
NSString *message = [NSString stringWithFormat:#"Users must enter this code to join the meeting: %#", meetingCode];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Meeting code"
message:message
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"Copy to clipboard", nil];
[alert show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == [alertView cancelButtonIndex])
{
NSLog(#"Code not copied");
}
else
{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
pasteboard.string = meetingCode;
NSLog(#"Code copied");
}
}
and this one:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
AgendaModel* agenda = _meeting.agenda[indexPath.row] ;
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:agenda.id,#"id",agenda.name,#"name", nil];
NSString *message = [NSString stringWithFormat:#"Are you sure that you want to delete : %#?", agenda.name];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:message
delegate:self
cancelButtonTitle:#"Close"
otherButtonTitles:#"Delete", nil];
[alert show];
NSString *delete_url = [NSString stringWithFormat:#"RestAgendas/delete.json"];
[_meeting.agenda removeObject:agenda];
[self.tableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[JSONAPI getWithPath:delete_url andParams:dict completion:^(id json, JSONModelError *err) {
NSLog(#"%#", json);
}];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == [alertView cancelButtonIndex])
{
NSLog(#"Agenda Won't Be Deleted");
}
else
{
NSLog(#"Agenda Will Be Deleted");
}
}
Now is the problem that I got the error: Duplicate declaration of method 'alertView:clickedButtonAtIndex'
How can I fix this? I tried some stuff I found here but I still can't make it work. Can someone help me?
Yep, like you said, giving a tag allows you to have multiple UIAlertView in one single view.
Same goes for UIActionSheet or UIAlertController
for future reference, even though this is probably a duplicate, simply create your alert like usual and add
myAlert.tag = 123; //any number of your choice
and in alertView:clickedButtonAtIndex
you can find it using a switch or some if's
if (alertview.tag = 123){
// this is my alert
}else if(alertview.tag = 333){
// this is my other alert
}
For what it's worth, I suggest using else if and not just if to avoid the whole method to be read everytime, and order your if's by decreasing likeliness of being called.
Note you can also give tags to buttons, views, cells, labels, and just pretty much every outlet you can find.

Alert before deleting UITableView cell - (UIAlertController) Swift or Objective-C

I want to delete a Table View cell, but before that action happens I want to give the user an alertview. I got this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Are you sure?"
delegate:self
cancelButtonTitle:#"NO"
otherButtonTitles:#"YES", nil];
[alert show];
[self.array removeObjectAtIndex:indexPath.row];//or something similar to this based on your data source array structure
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Nee"])
{
NSLog(#"Nothing to do here");
}
else if([title isEqualToString:#"Ja"])
{
NSLog(#"Delete the cell");
}
}
But now when I swipe to right on the cell and the delete button appears I got no AlertView. I only get the AlertView when I press on the delete button. When I press on the delete button the message appears but the cell is already been deleted.
How to make this work? So there is an AlertView when I swipe.
Regarding the sequence, everything is fine. commitEditingStyle will be called only when the delete button was pressed already. The point is that you are actually removing the object before the alert is responded to. Change it to this:
Add this to the .m file before #implementation:
#interface PutYourViewControllerClassNameHere
#property (strong, nonatomic) NSIndexPath *indexPathToBeDeleted;
#end
And then:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
self.indexPathToBeDeleted = indexPath;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Are you sure?"
delegate:self
cancelButtonTitle:#"NO"
otherButtonTitles:#"YES", nil];
[alert show];
// do not delete it here. So far the alter has not even been shown yet. It will not been shown to the user before this current method is finished.
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// This method is invoked in response to the user's action. The altert view is about to disappear (or has been disappeard already - I am not sure)
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"NO"])
{
NSLog(#"Nothing to do here");
}
else if([title isEqualToString:#"YES"])
{
NSLog(#"Delete the cell");
[self.array removeObjectAtIndex:[self.indexPathToBeDeleted row]];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:self.indexPathToBeDeleted] withRowAnimation:UITableViewRowAnimationFade];
}
}
Edit: This should compile, despite minor syntax errors probably.
General assupmtion: You are dealing with one section only. At least only with one section within deletions are possible.
iOS 8 +
iOS 8 introduced UIAlertController. This allows you to write your delete and cancel code in completion blocks instead of in delegate methods (as per -clickedButtonAtIndex of the old UIAlertView).
Swift 3
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let alertController = UIAlertController(title: "Warning", message: "Are you sure?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (action) in
self.tableView.deleteRows(at: [indexPath], with: .fade)
})
alertController.addAction(deleteAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
}
Objective-C
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Warning" message:#"Are you sure?" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:#"Delete" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self.tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}];
[alertController addAction:deleteAction];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(#"Don't do anything");
}];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
}
Youre calling the Alert when the deleting action is already taking place....
Put it in:
-(void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Waarschuwing!"
message:#"Weet je zeker dat je het vak: lalaal wilt verwijderen?"
delegate:self
cancelButtonTitle:#"Nee"
otherButtonTitles:#"Ja", nil];
[alert show];
}
That will call the alert when the cell is swiped and before the button is pressed.
iOS 9.0 and Swift 2.3
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let alertController = UIAlertController(title: "Warning!", message: "You're about to delete this stuff right meow.", preferredStyle: .Alert)
let delete = UIAlertAction(title: "Do it.", style: .Destructive, handler: { action in
tableView.beginUpdates()
//delete from your datasource!
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
tableView.endUpdates()
})
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: { action in
//this is optional, it makes the delete button go away on the cell
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
})
alertController.addAction(delete)
alertController.addAction(cancel)
presentViewController(alertController, animated: true, completion: nil)
}
}

UIAlertView EXC_BAD_ACCESS error

I cannot figure out why I keep getting an EXC_BAD_ACCESS error on the line below in the action sheet method:
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:numberFinal]];
I added my code, but I just don't see why its being over released.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
number = [[results objectAtIndex:indexPath.row]objectForKey:#"phone"];
number = [number stringByReplacingOccurrencesOfString:#"-" withString:#""];
numberFinal = [NSString stringWithFormat:#"tel:%#",number];
//tel:1234567890
NSLog(#"NUMBER:%#",numberFinal);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UIAlertView *alert2 = [[[UIAlertView alloc]initWithTitle:#"Call" message:#"Call This Person?" delegate:self cancelButtonTitle:#"NO" otherButtonTitles:#"YES", nil]autorelease];
alert2.tag = kAlertViewTwo;
[alert2 show];
// [alert2 release];
}
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if(actionSheet.tag == kAlertViewOne) {
if (buttonIndex == 0)
{
}else{
}
}
else if(actionSheet.tag == kAlertViewTwo) {
if (buttonIndex == 0)
{
//ok button clicked - close alert
}
else
{
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:numberFinal]];
}
}
}
Try do an NSLog before the line and print numberFinal to see if it allocated ok.
Make sure your class is a UIAlertView Delegate
Try the same line of code in another function to see if it causes the same issue. It might be because you are accessing sharedApplication from within the UIAlertView click event.
Hope that helps :)

Show or dismiss a specific ViewController after performing an action

I want to show a specific ViewController (or dismiss) a View after performing an IBAction in my iPhone App. I've tried
[self.parentViewController.parentViewController dismissModalViewControllerAnimated:YES];
However this does not appear to do anything once the action has been performed.
A bit more information:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
if (selectedCell.tag == 1)
{
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:#"Are you sure you want to delete this project?"
delegate:self cancelButtonTitle:#"No" destructiveButtonTitle:#"Yes, I’m Sure" otherButtonTitles:nil];
[actionSheet showInView:self.view];
}
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex != [actionSheet cancelButtonIndex])
{
[self.tableView beginUpdates]; // Avoid NSInternalInconsistencyException
// Delete the project object that was swiped
Project *projectToDelete = self.project;
NSLog(#"Deleting (%#)", projectToDelete.name);
[self.managedObjectContext deleteObject:projectToDelete];
[self.managedObjectContext save:nil];
}
}
I want the current view to disappear when a user presses the Yes button on the actionsheet.
// Assume we are inside a UIViewController (or a subclass)
DestinationController *destinationController = [[DestinationController alloc] init];
[self presentModalViewController:destinationController animated:YES];
...
// Assume we are now in destination controller
// Dismiss
[self dismissModalViewControllerAnimated:YES];
I needed to go all the way back to the first (or, root) view in my navigation stack.
All I need to do was use this method:
[controller.navigationController popToRootViewControllerAnimated:YES];
Another way to show and dismiss a view controller is with pushViewController and popViewController.
To show a viewController:
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
// Uses a horizontal slide transition. Has no effect if the view controller is already in the stack.
And to dismiss:
-(UIViewController *)popViewControllerAnimated:(BOOL)animated; // Returns the popped controller.

Displaying an alert view when a table row is tapped

I have a table view with a list of strings as follows:
String1
String2
String3
String4
I want to make one of these the default, i.e., when the user taps "String3", an alert view should pop up asking if they want to make that item the default.
How would I implement this alert view in my table view controller?
First you'll want to define an instance variable to keep track of the string that's currently selected. Something like this in your header file will be fine.
NSString *selectedString;
Next, in your tableView:didSelectRowAtIndexPath: delegate method create an alert view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedString = [stringArray objectAtIndex:indexPath.row];
NSString *title = [NSString stringWithFormat:#"Make %# default?", selectedString];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:#"" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alert show];
[alert release];
}
To save the value after the user taps a button in the alert you should use the UIAlertView delegate.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 2)
{
//Do something with selectedString here
}
}
I don't have enough reputation to comment here. But your if statement should be:
if (buttonIndex == 1)
{
//Do something with selectedString here
}
That is to say if you want to do something when the yes button is clicked. Anyways, thanks for the quick tutorial, and apart from that slight typo it worked perfectly.