Simple question on UIAlertView and UIAlertViewDelegate - iphone

I hope this is pretty straight-forward. As you'll see by my code, I'm simply trying to get a UIAlertView button-press to pop me back to the root view.
I don't get any compile errors or warnings, and when I run the app, the "RedeemCoupon" method is called in the IBAction, and the UIAlertView pops up as it should, but it doesn't seem the "doneRedeeming" method gets called at all - I don't see anything from NSLog (yes I'm aware that I am setting buttonIndex to 0 - once I get this working I'll fix it). So, basically it doesn't work. I click the "cancel" button and the alert just goes away.
By the way I'm not sure if this matters, but this "RedeemCouponViewController" view is number 4 on the stack, and it was added by use of presentModalViewController in the previous view.
I'm open to other ways of doing this if needed - all suggestions welcome!
Thanks in advance!
// RedeemCouponViewController.h
#interface RedeemCouponViewController : UIViewController <UIAlertViewDelegate> {
// RedeemCouponViewController.m
- (IBAction) redeemYes: (UIButton*) sender {
CouponRedeem *redeem = [[CouponDatabase database] couponRedeem:_uniqueId];
[redeem release];
UIAlertView *doneRedeeming = [[UIAlertView alloc]initWithTitle:#"Coupon Redeemed!"
message:#"Thanks for shopping!"
delegate:self
cancelButtonTitle:#"Back to Main Menu"
otherButtonTitles:nil];
[doneRedeeming show];
[doneRedeeming release];
}
-(void) doneRedeeming: (UIAlertView *) doneRedeeming clickedButtonAtIndex: (NSInteger)buttonIndex {
if (buttonIndex = 0) {
NSLog(#"doneRedeemed method called");
[self.navigationController popToRootViewControllerAnimated:YES];
} else {
//do nothing
}
}

You want to have
if (buttonIndex == 0) {
in place of
if (buttonIndex = 0) {
The former checks for equality whereas the latter assigns.
Also, you want to have
– alertView:clickedButtonAtIndex:
where you have
- doneRedeeming:clickedButtonAtIndex:

You need to use UIAlertViewDelegate methods:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {}
not
-(void) doneRedeeming: (UIAlertView *) doneRedeeming clickedButtonAtIndex: (NSInteger)buttonIndex {}

Use the delegate method -alertView:didDismissWithButtonIndex: to listen for your cancel button index

#PengOne's answer is correct: your problem is this:
if (buttonIndex = 0) {
You said
I know it's not correct, but I just
wanted to be sure the statement was
true for now...
But buttonIndex = 0 evaluates to 0, making it equivalent to
if (0)
The code within that block will never execute, regardless of the value of buttonIndex. If you really want to do it unconditionally, change the if to if( 1 ), or just take the if out.
This would have been trivial to spot if you ran this code in the debugger. You might think you know what your code is doing, but if you don't watch it run, you don't.

Related

How to create UIAlertView with multiple options?

I am trying to work out how I can use a UIAlertView to do more than one command.
Basically, in my ViewController, there is already an alertView, however I am now adding some storekit files, which, require there own alertView (to tell it whether to purchase the in-app or cancel etc.
Here is original alertView code;
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[alertView dismissWithClickedButtonIndex:0 animated:NO];
if (buttonIndex == 1) {
[g_GameUtils removeAlbumFolder:deleteIndex];
[g_GameUtils readAllData];
[g_GameUtils getAlbumFolderList];
[m_pTable reloadData];
}
}
And here is what I need also - they are both called alertView so I cannot use both like this, is there a way to combine them? Or is it better to call one of them alertView2 ? If so, how does it know which one to call for the particular alert?
Thanks in advance!
Chris
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0){
//cancel button clicked. Do something here or nothing here
}
else{
//other button indexes clicked
[[MKStoreManager sharedManager] buyFeature:#"com.davis.apptoken.buy"];
}
}
Also you can use alertView.tag = 1; and alertView2.tag = 2; and add appropriate conditions to delegate:
if (alertView.tag == 1)
{
// First alert
}

Alert View - How to use clickedButtonAtIndex:

I have this alert view (disclaimer) that pop up when app finish launching. It works (my app is much slower now), but I also want to exit from the app if the user press no, thanks. I think I should use clickedButtonAtIndex:.
1. Can somebody help me on this?
2. is viewDidLoad the best method to fire the alertView when the application start?
3. is there any reason why now my app take more time to start when I build and run it?
-(void)viewDidLoad {
UIAlertView *disclaimer = [[UIAlertView alloc] initWithTitle: #"DISCLAIMER" message:#"This Application is provided without any express or implied warranty. Errors or omissions in either the software or the data are not guaranteed against. The application is not intented to replace official documentation or operational procedure. In no event shal the developer be held liable for any direct or indirect damages arising from the use of this application" delegate:self cancelButtonTitle:#"No, thanks" otherButtonTitles:#"Accept", nil];
[disclaimer show];
[disclaimer release];
[super viewDidLoad];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 0)
// Do something
else
// Some code
}
or
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 0)
// Do something
else
// Some code
}
Make sure your class conforms to the UIAlertViewDelegate protocol.
And I dont think exiting from the app is a good approach though.You should only let the user close the application by pressing the home button. It's against the default behavior which the user expects every app to follow.
Try this magic:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(alertView.cancelButtonIndex == buttonIndex){
// Do cancel
}
else{
// Do the real thing
}
}

UIAlertView exits EXC_BAD_ACCESS error

I have such error: when I click navigationbar.backItemButton I'm showing UIAlertView with two buttons. When I press on any of them application terminates just with EXC_BAD_ACCESS. Method - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex don't called. How can I solve it? Thanx!
//h - file
#interface DetailsTableViewController : UITableViewController <UITextFieldDelegate, UIAlertViewDelegate>
//m - file
- (void)viewWillDisappear:(BOOL)animated
{
//if changes unsaved - alert reask window
if (isDirty)
{
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Save changes?"
message:#"Press YES if you want to save changes before exit, NO - other case."
delegate: self
cancelButtonTitle: #"NO"
otherButtonTitles: #"YES", nil];
[message show];
[message autorelease];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex: buttonIndex];
if([title isEqualToString: #"YES"])
{
[self saveBtnUserClick];
}
}
I think the problem is that after you tapped back button your current controller is removed from navigation stack and deallocated, so when alert tries to call its delegate methods it calls them on deallocated object which results in EXC_BAD_ACCESS error. To workaround the problem I see 2 obvious options (although there may be better solutions):
Extra retain your controller somewhere (in previous controller may be), but you need to find way to release it when you're done.
Create your custom button instead of standard "back" and just show alert when it tapped. Then in alert's delegate method pop your current controller from navigation stack.
Try Changing delegate to nil instead of self. It fixed my issue.
Is your view controller implementing the UIAlertViewDelegate? If not, add in you interface declaration before the { starts.
Also try NSLogging inside the clickedButtonAtIndex method and print the buttonIndex values and see the console.
Edit: Reading your post again, I guess you indeed have missed the UIAlertViewDelegate in your interface declaration.
Probably [message autorelease];
is you mistake use
[message release];
Because you have used [[UIAlertView alloc] init.....]; there for you should release the memory.
autorelease is something will work with the structure which memory is compiler dependent or you have not given the memory manually.
Enjoy.
"Try Changing delegate to nil instead of self. It fixed my issue." worked for me. Thanx

Using UIAlertView in a manner similar to Windows' MessageBox()?

I'm new to the iPhone and I would like to be able to use UIAlertView in a manner similar to the Windows MessageBox() or the MessageDlg() in Delphi.
For example, I have a method that needs to ask the user for confirmation on something, and proceed based on their response.
E.g. (pseudocode):
-(void)doSomething
{
[doStep1];
[doStep2];
var myValue = [getDefaultValue];
if (myValue = nil)
{
if [promptUser(#"No value in setting. Use the default value?")] //UIAlertView here?
{
myValue = #"defaultValue";
}
else
return; // bug out of the routine 'cause we have no value.
}
[doStep3 withValue:myValue];
}
Or, put put it another way- is there a way of using UIAlertView to ask the user a question within a routine as a way of controlling the logic flow of that routine?
There's no reason to subclass UIAlertView at all. That's what delegates are for. All you need is a class (such as your view controller) supporting the UIAlertViewDelegate protocol, and set the UIAlertView's delegate property to that class. You then implement the alertView:clickedButtonAtIndex: method in this class, and the alertViewCancel: method if you want to specifically handle cancellations differently.
You can read more about it in the UIAlertView documentation and in the UIAlertViewDelegate documentation.
I have no idea what MessageDlg() is, but you can certainly subclass UIAlertView and handle the dialog response, based on which button is pressed, e.g.:
Set up the UIAlertView subclass header:
//
// ARReachabilityAlertView.h
//
#import <UIKit/UIKit.h>
#interface ARReachabilityAlertView : UIAlertView <UIAlertViewDelegate> {
}
#end
Set up the UIAlertView subclass implementation:
//
// ARReachabilityAlertView.m
//
#import "ARReachabilityAlertView.h"
#implementation ARReachabilityAlertView
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setTitle:#"Error"];
[self setMessage:#"This application won't run without a network connection. Do you want to quit?"];
[self addButtonWithTitle:#"Quit"];
[self addButtonWithTitle:#"Continue"];
[self setDelegate:self];
}
return self;
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0)
exit(0); // quit application if "Quit" is pressed; otherwise, do nothing
}
- (void) drawRect:(CGRect)rect {
[super drawRect:rect];
}
- (void) dealloc {
[super dealloc];
}
#end
Note the alertView:clickedButtonAtIndex: delegate method. This handles the conditionals you use to decide how the application proceeds. You can send an NSNotification from here, or call a method in the application delegate, whatever you want.
In my example, this UIAlertView is instantiated if there is no network connection, and the application is closed if the user clicks "Quit" in the alert view. Otherwise, if the user clicks "Continue" the application keeps running as usual.
Note that the implementation of the subclass requires the drawRect: method be called. I'm not sure if this is a bug or not, since I'd expect the drawRect: method to be called in the super-class; I filed a bug report with Apple on this one, but I haven't heard anything. Comment it out if you want to see what the effect will be — it's kind of interesting.
I believe you're looking to use UIAlertView as a modal alert box (in the sense that you'd like your code to stop running until the user makes a selection). There's no easy way to do this, and it's really recommended that you NOT code for the iPhone in this manner. I think Alex's explanation is a good solution.
To do this, what you can do is to run the mainloop manually. I have not managed to stop the mainloop directly, so I instead run the mainloop for 0.5 seconds and wait until the user responds.
I ran into this question while researching the problem for a C#/MonoTouch user on the iPhone. The sample below is written for MonoTouch/C# but should be trivial to translate to Objective-C
The following function shows how you could implement a modal query with the above approach:
int WaitForClick ()
{
int clicked = -1;
var x = new UIAlertView ("Title", "Message", null, "Cancel", "OK", "Perhaps");
x.Show ();
bool done = false;
x.Clicked += (sender, buttonArgs) => {
Console.WriteLine ("User clicked on {0}", buttonArgs.ButtonIndex);
clicked = buttonArgs.ButtonIndex;
};
while (clicked == -1){
NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.5));
Console.WriteLine ("Waiting for another 0.5 seconds");
}
Console.WriteLine ("The user clicked {0}", clicked);
return clicked;
}
Just a quick tip: if you want a class to be a delegate for more than one UIAlertView, just use tag property to tell who is who:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Clear all" message:#"Are you sure you want to erase everything?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
alert.tag = ALERT_DELETE_TAG;
[alert autorelease];
[alert show];
Delegate methods are called with UIAlertView as first argument, and you can check who's the originator there.
Thanks to #miguel.de.icaza I found my solution like below (part of the code):
#interface MyClass: NSObject <UIAlertViewDelegate>
{
int confirmed;
}
- (BOOL) removeObjectAtIndex:(NSUInteger)index;
#end
#implementation
- (BOOL) removeObjectAtIndex:(NSUInteger)index
{
// delete confirmation alert
confirmed = -1;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Delete confirmation" message:#"Are you sure to delete object" delegate:self cancelButtonTitle:#"Yes" otherButtonTitles: #"No", nil];
alert.tag = 2;
[alert show];
[alert release];
// wait for confirm (0 or 1)
while (confirmed == -1) {
// this is what you need!!!
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
if (confirmed) {
[myObjects removeObjectAtIndex:index];
return YES;
}
else
return NO;
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch (alertView.tag) {
case 1:
// ...
break;
case 2:
if (buttonIndex == 0) // delete confirmed
confirmed = 1;
else // dismiss
confirmed = 0;
break;
default:
break;
}
}
#end

Using one delegate to manage two UIActionSheets

I have two UIActionSheets and I am thinking that I will control them with one delegate (the UIViewController that instantiates them). The delegate will capture an actionSheet call and try to figure out which of the two threw the event.
I tried to get the modalView's title to differentiate, but it seems to be invalid...
Should this work?
If not, is there some other way to distinguish which UIActionSheet did the event?
Or do I need to create two different classes that will be separate delegates for each UIActionSheet?
Thanks in advance.
I think you need the tag property of the UIActionSheet.
Something like:
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle ... ];
actionSheet.tag = 10;
[actionSheet showInView:self.view];
Then in your delegate:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (actionSheet.tag) {
case 10:
...
}
}
tag is a property of UIView and can be set in Interface Builder for components that appear there too. Quite handy, though I've never actually used it in this context myself.
Delegate methods in Cocoa include the sending object for this purpose. Keep a reference to each of your action sheets as an instance variable in your controller class, and you can compare this to the actionSheet parameter in your delegate methods to decide what actions you need to perform.
Using the view's tag property would work, but it would be easier to keep a reference. The tag property is meant to help you find a view if you're looking through a hierarchy of sub-views and don't have a reference to to the object you need.
You should use the actionSheet pointer passed to the delegate's method as Marc said. For example:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(actionSheet == myDoSomethingActionSheet) {
if(buttonIndex == 0) {
[self doThingA];
return;
}
if(buttonIndex == 1) {
[self doThingB];
return;
}
}
if(actionSheet == myOtherActionSheet) {
if(buttonIndex == 3) {
[self doImportantThing];
return;
}
}
}