Displaying an alert view when a table row is tapped - objective-c

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.

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.

Prevent from UIAlertView to repeat when click on tableViewCell

I'm kinda stuck with a stupid problem, in my app when a user press on a TableViewCell he can change some value in the server, before i am sending the information to server i want to show UIAlertView to the user to make sure he wants to do that.
so in didSelectRowAtIndexPath:
i wrote this
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Caution!" message:#"bla bla " delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
[alert show];
then its calls the UIAlertView method clickedButtonAtIndex:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
NSString *url = #"some url";
NSDictionary *paramerter = #{#"parameter":parameter};
[self.jsonHandler startParseWithParameters:paramerter andUrlAdress:url withCompletion:^(int errValue) {
if (errValue == 1) {
NSLog(#"ERROR");
}else if (errValue == 0){
[self AlertViewWithTitle:#"success" andMessage:#"success"];
}
}];
}
}
the problem is that the if (buttonIndex == 0) called for every cell and change them all!
and i get UIAlertView message for every cell in the tableView.
how can i prevent it?
EDIT
Ok the problem caused because i use the same buttonIndex for both UIAlertViews
i solved it using this:
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"buttonString"]){
//Do what ever you need
}
hope someone find it helpful.

Deleting a row in a UITableViewController with an alert confirmation

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.

iOS 5 Black screen after segue

I have an iOS 5 application with a storyboard and two scenes. Scene 1 is a selection list while Scene 2 shows details for each selection
In Scene 1 I need to pass a variable and I do that with:
//Handles the selection
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
Guests *myGuests =[guestList objectAtIndex:[indexPath row]];
selectedGuest = [[NSString alloc] initWithFormat:#"You have selected %#", myGuests.guestID];
[self performSegueWithIdentifier:#"TGG" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"TGG"]){
GuestDetailsViewController *vc = [segue destinationViewController];
[vc setGuestSelected:selectedGuest];
}
}
In Scene 2 (details) I use this to verify that the right variable was received like this
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
NSString *msg = [[NSString alloc] initWithFormat:#"You have selected %#", guestSelected];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Selection" message:msg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
nameCell.textLabel.text= msg;
[super viewDidLoad];
}
All of this works fine and the alert box shows the right variable and there is a transition to the new scene. HOWEVER, the alert box displays a total of 5 times over & over and, once completed, the whole window is black. Nothing from my scene (Scene 2) is displayed at that point. So I know I have the right segue, it transitions as desired, I just can't see anything on my screen.
i ran into a situation very similar. i had unintentionally un-commented the method:
-(void)loadView
i believe this method overrides the IB interface and created it from this code instead. check to make sure it is either removed or commented out IMHO.

How to write event handlers for buttons in UIAlertView?

Say I have a alert view like follows in obj c
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"title" message:#"szMsg" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:#"download"];
[alert show];
[alert release];
Now we have 2 buttons on the alert view (Ok & Download), how to write an event handler for the Download one?
First you will need to add the UIAlertViewDelegate to your header file like below:
Header file (.h)
#interface YourViewController : UIViewController<UIAlertViewDelegate>
Implementation File (.m)
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"title" message:#"szMsg" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:#"download"];
[alert show];
[alert release];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0)
{
//Code for OK button
}
if (buttonIndex == 1)
{
//Code for download button
}
}
Now that most iOS devices have firmare versions with blocks support it’s an anachronism to use the clumsy callback API to handle button presses. Blocks are the way to go, see for example the Lambda Alert classes on GitHub:
CCAlertView *alert = [[CCAlertView alloc]
initWithTitle:#"Test Alert"
message:#"See if the thing works."];
[alert addButtonWithTitle:#"Foo" block:^{ NSLog(#"Foo"); }];
[alert addButtonWithTitle:#"Bar" block:^{ NSLog(#"Bar"); }];
[alert addButtonWithTitle:#"Cancel" block:NULL];
[alert show];
Declare your UIAlertViews as known.
UIAlertView *alertLogout=[[UIAlertView alloc]initWithTitle:#"Title" message:#"Stop Application?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[alertLogout show];
[alertLogout release];
set delegate to self and implement this method.
-(void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(actionSheet== alertLogout) {//alertLogout
if (buttonIndex == 0){
}else if(buttonIndex==1){
}
}else if (actionSheet==alertComment) {//alertComment
if (buttonIndex==0) {
}
}
}
Stack's and Guillermo Ortega's answer is probably what you would use with a couple of UIAlertView but not for ten. I use to use BlocksKit which is kind of the same as Lambda stuff which is what soul suggested. That is a good option too, although if you have too many nested blocks you will start seeing the demerits of it (Aside from the fact you will be relying in another library).
The usual way of handling several stuff would be to have a handler object. (
#interface MyAlertViewDelegate : NSObject <UIAlertViewDelegate> #end) make that object the delegate of the alert view and make sure the object is alive at least until the alert view is dismissed.
This will certainly work, but could be too much work...
What follows is what I came up with; IMO it is simpler and there is no need of any thirdParty library, or an ivar per UIAlertView. Just one extra object (#property (nonatomic, strong) NSArray *modalActions) to store the actions the current UIAlertView will cause to perform
Showing an UIAlertView and reacting accordingly
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle
message:#"Blah blah"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:b1, b2, b3, nil];
// Add one selector/action per button at the proper index
self.modalActions = #[
[NSNull null], // Because indexes of UIAlertView buttons start at 1
NSStringFromSelector(#selector(actionForAlertViewButton1)),
NSStringFromSelector(#selector(actionForAlertViewButton2)),
NSStringFromSelector(#selector(actionForAlertViewButton3))];
[alertView show];
The delegate method:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.cancelButtonIndex != buttonIndex) {
[self performModalActionAtIndex:buttonIndex];
}
}
The part that actually performs the action:
- (void)performModalActionAtIndex:(NSInteger)index
{
if (-1 < index && index < self.modalActions.count &&
[self.modalActions[index] isKindOfClass:[NSString class]]) {
SEL action = NSSelectorFromString(self.modalActions[index]);
NSLog(#"action: %#", self.modalActions[index]);
if ([self respondsToSelector:action]) {
// There is a situation with performSelector: in ARC.
// http://stackoverflow.com/questions/7017281/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:action];
#pragma clang diagnostic pop
}
self.modalActions = nil;
}
Reusable for UIActionSheets too
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:title
delegate:self
cancelButtonTitle:cancelButton
destructiveButtonTitle:nil
otherButtonTitles:button1, button2, button3, nil];
// Similarly, add one action per button at the proper index
self.modalActions = #[
NSStringFromSelector(#selector(actionForActionSheetButton1)),
NSStringFromSelector(#selector(actionForActionSheetButton2)),
NSStringFromSelector(#selector(actionForActionSheetButton3))];
The delegate method:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (actionSheet.cancelButtonIndex != buttonIndex) {
[self performModalActionAtIndex:buttonIndex];
}
}
Why this works:
This works because of two reasons:
First, I never present two UIAlertView that have a delegate at the same time. (IMO you should't, it doesn't look good). Second, because in my case (as 90% of the cases) the target of the actions is always the same object (in this case: self). Even if you don't meet above conditions you can even use this approach with some modifications:
If you show two or more UIAlerViews or UIActionSheets at the same time (possible in the iPad) Use a dictionary with to store one array of actions associated with a certain UIAlertView/UIActionSheet.
If the target of the actions is not self, they you need to store pairs (target and the action) in the array. (Something to simulate UIButtons addTarget:action:...).
In either case, for storing the target and/or UIActionSheet/UIAlertView [NSValue valueWithNonretainedObject:] should become handy :)
First of all you declare UIAlertViewDelegate in .h file after put below code in .m file
- (void)alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1)
{
//put button action which you want.
}
}
Implement the UIAlertViewDelegate and make use of the delegate method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == 0) {
// Do something
}
else {
// Do something
}
}
UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:#"Data Saved" message:#"Choose more photos" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:Nil];
[alertView show];
[alertView release];
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex==0)
{
[self dismissModalViewControllerAnimated:YES];
}
}
in swift:
we can use this little block of code
let alert = UIAlertController(title: "Alert", message: "This is an alert message", preferredStyle: UIAlertControllerStyle.Alert)
let action = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {(action:UIAlertAction) in print("This is in alert block")
})
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)