I am displaying a UIAlertView to the user with an "OK" button on it, and when the user presses the button I would like my delegate method to perform an action. However currently when the user presses the "OK" button the application crashes with this error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0xb)
Here is my code, the alert view shows fine with the button etc, however as soon as the button is pressed this is the error I am getting. It's almost like a breakpoint but if I click the forward button nothing happens.
//.h
#interface ErrorHandling : NSObject <UIAlertViewDelegate> {
//.m
#define myAlertViewsTag 0
- (void)recivedErrorCode:(int)errorCode MethodName:(NSString *)methodName {
switch (errorCode) {
case 1: {
NSLog(#"ERROR = 1");
message = [[UIAlertView alloc] initWithTitle:#"Error 1:"
message:#"The supplied registration code does exist."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[SVProgressHUD dismiss];
[message show];
}
break;
case 2: {
NSLog(#"ERROR = 2");
message = [[UIAlertView alloc] initWithTitle:#"Error 2:"
message:#"Your registration is no longer valid."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
message.tag = myAlertViewsTag;
[SVProgressHUD dismiss];
[message show];
}
break;
default:
break;
}
}
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (message.tag == myAlertViewsTag) {
if (buttonIndex == 0) {
// Do something when ok pressed
NSLog(#"DONE 1");
} else {
// Do something for ok
NSLog(#"DONE 2");
}
} else {
// Do something with responses from other alertViews
NSLog(#"DONE 3");
}
}
update:
This is how I call the class the code above is in from my conneciton class.
// Do whatever you need to with the error number
ErrorHandling *errorHandling = [[ErrorHandling alloc] init];
[errorHandling recivedErrorCode:errorRecivedFromServerInt MethodName:methodName];
The problem is with the lifetime of your ErrorHandling instance. You create an instance and then call the recivedErrorCode: method. After that there are no other strong references to your errorHandling instance so it gets deallocated.
Meanwhile the alert view has made the instance its delegate. When you tap the button on the alert view, the alert view tries to contact its delegate. But the delegate has been deallocated and now points to garbage memory resulting in the crash.
The solution is to keep a longer lasting strong reference to the ErrorHandling instance. At least until after the alert view is dismissed.
BTW - Your method name has a typo - it should be receivedErrorCode:.
It is not at all clear from this code if this line:
if (message.tag == myAlertViewsTag)...
Actually points to anything valid. Is the message object still around even... I think you problems lies there, somewhere. Post more code that helps us understand this if you want more help.
In alertView:didDismissWithButtonIndex: you are checking the alert view's tag by using "message.tag".
It looks like you were intending for message to be an ivar in your class. A better way that should also solve your bad_access would be to use the alert view that is passed into the didDismissWithButtonIndex, rather than retaining the UIAlertView in your class. This code should do the trick:
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (alertView.tag == myAlertViewsTag) {
if (buttonIndex == 0) {
// Do something when ok pressed
NSLog(#"DONE 1");
} else {
// Do something for ok
NSLog(#"DONE 2");
}
} else {
// Do something with responses from other alertViews
NSLog(#"DONE 3");
}
}
Related
So I have the following code and when the round is over it pops up with the following UIAlertView. Now when the user pushes the leaderboards button I want it to run the - (void) statement I have that displays the leaderboards. Please not that this is all inside of a sprite kit view controller. How should I do this?
Also I have my void statement in a different class, is there anyway to bring that over via an #import?
- (void) gameEnded
{
// indicate our game state as stopped
_gameState = STOPPED;
// create a message to let the user know their score
NSString *message = [NSString stringWithFormat:#"You scored %d this time", _score];
// show the message to the user
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Game over!"
message:message
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:#"Leaderboards",nil];
[av show];
// reset the score tracker for the next game
_score = 0;
//reset playing area
[self removeAllBlocks];
[self addBlocks];
}
Here:
// show the message to the user
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Game over!"
message:message
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:#"Leaderboards",nil];
You need to set the delegate to 'self', or another class that you want be handling the alert view events as its delegate. That class must conform to the UIAlertViewDelegate, for example:
#interface YourController : YourControllerSuperClass <UIAlertViewDelegate>
Then, Xcode will probably suggest you to implement:
– alertView:clickedButtonAtIndex:
As this is the method called by the alert view on its delegate when a button is pressed.
Here you'll receive a reference to your alert view and the index of the pressed button.
With this, you can find which action was performed.
For example:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
// In case there are more then one alert view and a reference to each of the alert views are kept.
if(alertView == self.avAlertView){
switch(buttonIndex){
// Your button's cases!
}
}
}
Your current Controller should implement UIAlertViewDelegate, and call the method alertView:clickedButtonAtIndex: with the index of your leaderboard button.
Your CustomController.h:
#import <UIKit/UIKit.h>
#interface AdsEventViewController : UITabBarController<UIAlertViewDelegate>
#end
Your CustomController.m:
[...]
- (void) gameEnded
{
// indicate our game state as stopped
_gameState = STOPPED;
// create a message to let the user know their score
NSString *message = [NSString stringWithFormat:#"You scored %d this time", _score];
// show the message to the user
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Game over!"
message:message
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:#"Leaderboards",nil];
[av show];
// reset the score tracker for the next game
_score = 0;
//reset playing area
[self removeAllBlocks];
[self addBlocks];
}
[...]
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex == _LEADERBOARD_BUTTON_INDEX_ ){
// leaderboard method
}
}
I have an IF statement that checks labels to see if a label is empty, if it is show an alert.
if ([_DOBDate.text length] == 0)
{
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Cannot Proceed"
message:#"Please Submit your DOB and Gender"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
}
later on in the function I have perform segue, like this :
[self performSegueWithIdentifier:#"SetUptoMain" sender:self];
True, the alert does fire when the label is blank, but it also performs the segue. This perform segue line is not in the IF statement. So I would have thought it would have ran the IF statement and stayed there till I pressed OK. OK would be staying on the same view controller.
The segue is performed, is this due to Blocks ? any advice ?
So if the USER pressed Ok from the UIAlert the VC does not move, it stays where it was so the user can enter the details required.
This is my code :
- (IBAction)SettingsSave:(id)sender {
if ([_DOBDate.text length] == 0)
{
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Cannot Proceed"
message:#"Please Submit your DOB and Gender"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
}
more code...
then at the end
[self performSegueWithIdentifier:#"SetUptoMain" sender:self];
}
thanks
Seems to be an if/else logic issue. Read about it.
-(IBAction)yourAction:(id)sender
{
if ([_DOBDate.text length] == 0)
{
//Show your AlertView as you did
}
else
{
//put the rest of your code, including the performSegue
}
}
UIAlertView doesn't block execution. It runs asynchronously. Therefore the code path will go into your if statement, then continue past it.
If you only want the segue to be performed after the user presses the alert view button then you need to implement UIAlertViewDelegate.
In your header file add something like this:
#interface MyController : UIViewController <UIAlertViewDelegate>
When you create the alert view do it like this:
UIAlertView *message = [[UIAlertView alloc] initWithTitle: #"Cannot Proceed"
message: #"Please Submit your DOB and Gender"
delegate: self
cancelButtonTitle: #"OK"
otherButtonTitles: nil];
[message show];
And add this method to implement the UIAlertViewDelegate.
- (void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex {
// perform segue here. You can also check what button was pressed based on the button index.
}
EXTRA INFO: I must have asked wrong, I'd like the user to click the button and be redirected to Facebook!
I need to add a visit facebook button called "facebook" as swell in the code below! Right now I just have an ok button.
Also if possible could you help me understand, how I will be able to retrieve user information - e.g I'll ask them to add there email in the textfield, when they press ok then where will I store the email and obtain it, how does that work, and what do I need to read to find out?
Try to add your buttons in otherButtonTitles
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Refresh"
message:#"Are you want to Refresh Data"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Button1",#"Button2",#"Button3",
nil];
As per Duncan's answer you can use delegate and get which button is clicked.so on specific button's tap you can redirect to facebook page using below code.
UIAlertView *info = [[UIAlertView alloc]initWithTitle:#"Yup" message:#"You've won! Like Us on Facebook too" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:#"Facebook", nil];
info.tag = 10;
[info show];
So when the user presses the Facebook button the delegate method alertView:clickedButtonAtIndex will be called so at that time check alert.tag and then check if facebook button is tapped then show another alert.
Your Delegate Method should be
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 10)
{
switch (buttonIndex) {
case 0:
NSLog(#"Cancel");
break;
case 1:
NSLog(#"Facebook");
[self showAlertForRedirect];
break;
default:
break;
}
}
else if (alertView.tag == 20)
{
switch (buttonIndex) {
case 0:
NSLog(#"Cancel");
break;
case 1:
NSLog(#"OK");
[self RedirectNow];
break;
default:
break;
}
}
}
-(void)showAlertForRedirect
{
UIAlertView *info2 = [[UIAlertView alloc]initWithTitle:#"Note" message:#"Would you like to redirect on facebook?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
info2.tag = 20;
[info2 show];
}
-(void)RedirectNow
{
NSURL *fanPageURL = [NSURL URLWithString:#"fb://profile/yourid"];
if (![[UIApplication sharedApplication] openURL: fanPageURL])
{
NSURL *webURL = [NSURL URLWithString:#"https://www.facebook.com/yourpagename"];
[[UIApplication sharedApplication] openURL: webURL];
}
}
As the other poster said, you can add additional buttons by providing button titles in the otherButtonTitles parameter to the initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles: method.
You can set up your alert to have an input field by setting the alertViewStyle property of the button returned by the above method to UIAlertViewStylePlainTextInput
You need to pass in self (your view controller) as the delegate parameter.
Then you need to implement the method alertView:didDismissWithButtonIndex:
In that method, you will get the button index the user selected. You can use the textFieldAtIndex: method to get a pointer to the text field, and get the user-entered text.
I am working on an user activation errors, I have a NSObject class that gets call if an error is returned from the DB.
I show an alertview that has a method called when the user presses the UIButton on the alert view. This is what the method looks like.
//ErrorHandling.m
//
case 1: {
NSLog(#"ERROR = 1");
message = [[UIAlertView alloc] initWithTitle:#"Error 1:"
message:#"The activation request failed."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
message.tag = myAlertViewsTag;
[self performSelector:#selector(showAlertViewAndMessage) withObject:message afterDelay:0.3]; // set timer to give any huds time to load so I can unload them correctly
}
break;
//
- (void)showAlertViewAndMessage {
[SVProgressHUD dismiss];
[message show];
}
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (alertView.tag == myAlertViewsTag) {
if (buttonIndex == 0) {
if (receivedResponseData != nil) {
if (errorCodeValue == 1) {
[[self errorDataDelegate] passErrorDataToRoot:receivedResponseData];
}
// incase the user is further on in the navigationstack bring them back to the rootview
[self.currentNavigationController popToRootViewControllerAnimated:YES];
}
}
}
so far all this code works, accept for the delegate/protocol request... I have checked and double checked my code, however I think maybe I am missing something that maybe you can see. This is what my Delegate and Protocol looks like.
//errorHandling.h
#protocol RecivedErrorData <NSObject>
- (void)passErrorDataToRoot:(NSData *)errorData;
#end
//Protocol/delegate
__weak id <RecivedErrorData> errorDataDelegate;
//Protocol/delegate
#property (weak, nonatomic) id <RecivedErrorData> errorDataDelegate;
//errorHandling.m
//delegate / protocols
#synthesize errorDataDelegate;
[[self errorDataDelegate] passErrorDataToRoot:receivedResponseData];
//RootViewController.h
#import "ErrorHandling.h"
#interface RootViewController : UIViewController <RecivedErrorData> {
// error handling for activations
ErrorHandling *errorHandling;
//RootViewController.m
-(void)viewDidLoad {
errorHandling = [[ErrorHandling alloc] init];
[errorHandling setErrorDataDelegate:self];
}
#pragma ErrorProtocol
- (void)passErrorDataToRoot:(NSData *)errorData {
NSLog(#"WORKED");
}
So thats my code for the protocol and delegate, it almost works when the button is clicked it just never maked it to passErrorDataToRoot delegate method.
I am wondering if its an error in initialization, ErrorHandling.h is initialized originally when the app starts up inside the rootView, then when I get an error from a request I call ErrorHandling.m from a class called EngineRequest.m using alloc init etc... that's the only thing I can think of, that because of this extra allocation im dealing with another method but I am not sure this is the reason? I thought delegates and protocols were used to avoid this issue of reallocation.
I want to make a "terms and conditions" screen that pops up the very first time that app is opened. On this view I would add a button that says "agree," and upon clicking it the code would execute:
[self dismissViewControllerAnimated:YES completion:nil];
...and would go to the first view of the app.
I am currently using a Tab Bar Controller that has 4 ViewControllers. So basically, I just need to have some method in viewWillAppear on my first ViewController that checks for an NSUserDefault key:value. The first time the app opens, it will be zero. After they click agree, I'll set it to 1 and the bit of code would never execute again.
Can you please offer some code to accomplish the task of routing the view from the firstViewController's view to this alternate view controller upon loading the app?
Thanks!
In the viewWillAppear method in your FirstViewController, check NSUserDefaults then present your TermsViewController. After user click agree in TermsViewController, set NSUserDefaults then call
[self.presentingViewController dismissModalViewControllerAnimated:YES]
The use of the popover window can get complicated. Try something like the following if you have little experience with Objective-C.
- (void)viewDidLoad {
if ([termsvalue == 0]) {
NSString *msg = [NSString stringWithFormat:#"Do you agree with the terms of use?"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"- Confirmation -"
message:msg
delegate:self
cancelButtonTitle:#"Disagree"
otherButtonTitles:#"I agree", nil];
[alert setTag:100];
[alert show];
}
}
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView { // Validation
if ([alertView tag] == 100) {
return YES;
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { // Go
if ([alertView tag] == 100) {
if (buttonIndex == 1) {
// The user has agreed
}
}
}