UIAlertView Delegates - iphone

Can someone explain how the delegate to a UIAlertView works? Is it automatically called or do I have to call it? Eg:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

Let's say you showed an alert where the delegate was "self"
- (void)showAlert {
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:#"My Alert"
message:#"Do you want to continue?"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"No", #"Yes", nil];
[myAlert show];
[myAlert release];
}
In order for the following to work in your .m file:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
Your .h file will need to reference the UIAlertViewDelegate in the implementation statement like so:
#interface myViewController : UIViewController <UIAlertViewDelegate> {
}
This is what allows your .m file to respond to UIAlertViewDelegate method calls.

So long as you're correctly setting the delegate property of the UIAlertView and implementing the protocol, it will be automatically called when a user clicks on a button in your alert.
Take a look at the projects listed under "Related sample code" at http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIAlertViewDelegate_Protocol/UIAlertViewDelegate/UIAlertViewDelegate.html to see it in action.

Here is a wrapper for the delegate so that you can use blocks instead. The flow of execution will be the same but the flow of the code will be easier to follow. So, usage:
[YUYesNoListener yesNoWithTitle:#"My Title" message:#"My Message" yesBlock:^
{
NSLog(#"YES PRESSED!");
}
noBlock:^
{
NSLog(#"NO PRESSED!");
}];
...and here is the helper class:
typedef void(^EmptyBlockType)();
#interface YUYesNoListener : NSObject <UIAlertViewDelegate>
#property (nonatomic, retain) EmptyBlockType yesBlock;
#property (nonatomic, retain) EmptyBlockType noBlock;
+ (void) yesNoWithTitle:(NSString*)title message:(NSString*)message yesBlock:(EmptyBlockType)yesBlock noBlock:(EmptyBlockType)noBlock;
#end
#implementation YUYesNoListener
#synthesize yesBlock = _yesBlock;
#synthesize noBlock = _noBlock;
- (id) initWithYesBlock:(EmptyBlockType)yesBlock noBlock:(EmptyBlockType)noBlock
{
self = [super init];
if (self)
{
self.yesBlock = [[yesBlock copy] autorelease];
self.noBlock = [[noBlock copy] autorelease];
}
return self;
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0 && self.noBlock)
self.noBlock();
else if (buttonIndex == 1 && self.yesBlock)
self.yesBlock();
[_yesBlock release];
[_noBlock release];
[alertView release];
[self release];
}
- (void) alertViewCancel:(UIAlertView *)alertView
{
if (self.noBlock)
self.noBlock();
[_yesBlock release];
[_noBlock release];
[alertView release];
[self release];
}
+ (void) yesNoWithTitle:(NSString*)title message:(NSString*)message yesBlock:(EmptyBlockType)yesBlock noBlock:(EmptyBlockType)noBlock
{
YUYesNoListener* yesNoListener = [[YUYesNoListener alloc] initWithYesBlock:yesBlock noBlock:noBlock];
[[[UIAlertView alloc] initWithTitle:title message:message delegate:yesNoListener cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil] show];
}
#end

The alertView:clickedButtonAtIndex: method of the delegate is automatically called by UIAlertView. The init method for UIAlertView takes a delegate as one of the parameters. Just make sure to pass in an object that responds to alertView:clickedButtonAtIndex:.

Related

call actionSheet clickedButtonAtIndex make a crash

I want to put action Sheet to a reuse class, but when i click the action sheet, the app crash and the error is EXC_BAD_ACCESS.
MOTestAction.h
#import <Foundation/Foundation.h>
#interface MOTestAction : NSObject<UIActionSheetDelegate>
{
UIActionSheet * actionSheet;
}
- (id) initWithAction;
- (void) showInView:(UIView *) view;
#end
MOTestAction.m
#import "MOTestAction.h"
#implementation MOTestAction
- (id) initWithAction
{
self = [super init];
if (self) {
actionSheet = [[UIActionSheet alloc]initWithTitle:nil delegate:self cancelButtonTitle:#"Cancle" destructiveButtonTitle:nil otherButtonTitles:nil, nil];
}
return self;
}
- (void) showInView:(UIView *) view
{
[actionSheet showInView:view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex;
{
}
#end
The caller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically
MOTestAction * moActionSheet = [[MOTestAction alloc]initWithAction];
[moActionSheet showInView:self.navigationController.toolbar];
}

How to show a custom uialertview created in interface builder

I've created a custom uialertview in ib, with two button and a textfield.
Then i create a .h and a .m in this way:
#import <UIKit/UIKit.h>
#interface DialogWelcome : UIAlertView{
IBOutlet UITextField *text;
UIButton *ok;
UIButton *annulla;
}
#property(nonatomic,retain) UITextField *text;
- (IBAction)ok:(id)sender;
- (IBAction)annulla:(id)sender;
#end
and .m :
#import "DialogWelcome.h"
#implementation DialogWelcome
#synthesize text;
-(IBAction)ok:(id)sender{
}
-(IBAction)annulla:(id)sender{
}
#end
i've not implement methods cause in first i need to show it!
i call this in viewcontroller in this way:
- (void)viewDidLoad
{
[super viewDidLoad];
DialogWelcome *alert = [[DialogWelcome alloc] initWithTitle:#"Welcome"
message:nil
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:nil];
[alert show];
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*2.0);
dispatch_after(delay, dispatch_get_main_queue(), ^{
[alert dismissWithClickedButtonIndex:0 animated:YES];
});
[alert release];
}
This will show a normal uialertview empty with a title. why it doesn't show my custom uialertview?
From the UIAlertView Class Reference:
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
So, what you're trying to do is explicitly not allowed.

Resign keyboard immediately after button press

I have a button, "getResponse", that makes a XMLRPC call to a server after you put in an IP address in a UITextField. Id like the "getResponse" button to first resign the keyboard if its up THEN make the call to the server.
As it is now, if the keyboard is up, it will make the call to the server, retrive the info or the error if no valid server found, THEN resign the keyboard.
I know its probably something very easy but I can't for the life of me figure it out. Any help would be great, thanks!
Code:
h.
#import <UIKit/UIKit.h>
#import "XMLRPCResponse.h"
#import "XMLRPCRequest.h"
#import "XMLRPCConnection.h"
#interface SecondViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate> {
IBOutlet UILabel *helloResponse;
IBOutlet UILabel *SumCountsResponse;
IBOutlet UITextField *serverInput;
IBOutlet UIPickerView *pickerView;
IBOutlet UILabel *pickerTrap;
IBOutlet UIAlertView *alert;
NSMutableArray * pickerViewArray;
int trapSelected;
}
#property(nonatomic,retain) IBOutlet UILabel *helloResponse;
#property(nonatomic,retain) IBOutlet UILabel *SumCountsResponse;
#property(nonatomic,retain) IBOutlet UITextField *serverInput;
#property(nonatomic,retain) IBOutlet UIPickerView *pickerView;
#property(nonatomic,retain) IBOutlet UILabel *pickerTrap;
#property(nonatomic,retain) IBOutlet UIAlertView *alert;
- (IBAction)getResponse:(id)sender;
- (IBAction)serverInputReturn:(id)sender;
- (IBAction)backgroundTouched:(id)sender;
#end
m.
#import "SecondViewController.h"
#implementation SecondViewController
#synthesize helloResponse;
#synthesize SumCountsResponse;
#synthesize serverInput;
#synthesize pickerView;
#synthesize pickerTrap;
#synthesize alert;
-(IBAction)serverInputReturn:(id)sender { //resign keyboard on 'return' button
[sender resignFirstResponder];
}
-(IBAction)backgroundTouched:(id)sender { //resign keyboard on background touch
[serverInput resignFirstResponder];
}
-(IBAction)getResponse: (id) sender {
[self serverInputReturn:self]; //trying to call method to resign keyboard first
//setup IP call for XMLserver
NSString *server = serverInput.text;
NSString *http = #"http://";
server = [server stringByAppendingString:#":8080/RPC2"];
server = [http stringByAppendingString:server];
//Begin calls to XMLRPC server for data return
XMLRPCRequest *requestHello = [[XMLRPCRequest alloc] initWithHost:[NSURL URLWithString:server]];
[requestHello setMethod:#"hello" withObjects:[NSArray arrayWithObjects: nil]];
NSString *resultHello = [self executeXMLRPCRequest:requestHello];
[requestHello release];
XMLRPCRequest *requestSumCounts = [[XMLRPCRequest alloc] initWithHost:[NSURL URLWithString:server]];
[requestSumCounts setMethod:#"SumCountsString" withObjects:[NSArray arrayWithObjects: nil]];
NSString *resultSumCounts = [self executeXMLRPCRequest:requestSumCounts];
[requestSumCounts release];
XMLRPCRequest *requestTrapCountX = [[XMLRPCRequest alloc] initWithHost:[NSURL URLWithString:server]];
[requestTrapCountX setMethod:#"TrapCountString" withObjects:[NSArray arrayWithObjects: [NSNumber numberWithInt:trapSelected], nil]];
NSString *resultTrapCountX = [self executeXMLRPCRequest:requestTrapCountX];
[requestTrapCountX release];
if (![resultHello isKindOfClass:[NSString class]]){
alert = [[UIAlertView alloc] initWithTitle:#"Error: Invalid Server Address" message:#"Please Check Server Address and Try Again" delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[alert show];
[alert release];
}
else{
helloResponse.text = resultHello;
SumCountsResponse.text = resultSumCounts;
pickerTrap.text = resultTrapCountX;
}
}
}
- (id)executeXMLRPCRequest:(XMLRPCRequest *)req {
XMLRPCResponse *userInfoResponse = [XMLRPCConnection sendSynchronousXMLRPCRequest:req];
if ([userInfoResponse isKindOfClass:[NSError class]]) {
return alert;
}
else{
return [userInfoResponse object];
}
}
Instead of
[self serverInputReturn:self];
Try
[self serverInputReturn:self.serverInput];
You need to pass the UITextField not the UIViewController.
Make sure that you are setting the UITextField's delegate to the UIViewController via
self.serverInput.delegate = self;

displaying a dialable phone number withing the text for a UIAlertView

is there a way that part of the text that is being displayed in an iphone UIAlertView will be a phone number that when clicked will be dialed?
maybe using tel: somehow ?
If you like to implement the dataDetectorType in your message text there is no native way to do it.
The only way is to subclass the UIAlertView and customize the init method like this :
- (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
self = [super initWithTitle:title message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil];
if (self) {
CGRect alertFrame = [self frame];
UITextView myTextView = [[UITextView alloc] initWithFrame:CGRectMake(alertFrame.origin.x + 10, alertFrame.origin.y + 44, 200, 44)];
[myTextView setEditable:NO];
[myTextView setBackgroundColor:[UIColor clearColor]];
[myTextView setDataDetectorTypes:UIDataDetectorTypeAll];
[myTextView setText:#"http://www.apple.com"]; // Use your original message string from init
[self addSubview:myTextView];
[myTextView release]
}
return self;
}
I tested it right now and it works but you need to spend a little bit to make it presentable :P
Maybe using the way posted by Jhaliya is quickly and more clean.
Yes, This is possible by setting the delegate ( UIAlertViewDelegate ) of your UIAlertView. Read the message and title from your UIAlertView by using below properties.
#property(nonatomic, copy) NSString *message
#property(nonatomic, copy) NSString *title
You need to implement the delegte method in order to get the info for which button pressed.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
you could also follow link for getting pressed button index and dial an Phone number by Using tel: .
So, your code could be something like below .
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//Access `title` and `message` of your **UIAlertView**
NSString* alertTitle = alertView.title;
NSString* alertMessage = alertView.message;
// Formatted the phone number and assign it to a string.
NSString* myFormattedPhNumber = /*Use StringWithFormat function of NSString */;
if (buttonIndex == 0)
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:myFormattedPhNumber];
}
}

How to safely pass a context object in an UIAlertView delegate?

In my application I use a UIAlertView to display to the user a message and some options. Depending on the button pressed, I want the application to perform something on an object.
The sample code I use is...
-(void) showAlert: (id) ctx {
UIAlertView *baseAlert = [[UIAlertView alloc]
initWithTitle: title
message: msg
delegate:self
cancelButtonTitle: cancelButtonTitle
otherButtonTitles: buttonTitle1, buttonTitle2, nil];
//baseAlert.context = ctx;
[baseAlert show];
[baseAlert release];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
id context = ...;//alertView.context;
[self performSelectorOnMainThread:#selector(xxx:) withObject: context waitUntilDone: NO];
}
}
Is there any way to pass an object into the delegate as a context object? or maybe some other way?
I could add the property on the delegate but the same delegate object is being used by many different alert views. For this reason I would prefer a solution where the context object is attached to the UIAlertView instance and carried across to the delegate as part of the UIAlertView object.
I still think storing it locally is the best solution. Create a class local NSMutableDictionary variable to hold a map of context objects, store the context with UIAlertView as the key and the context as the value.
Then when the alert method is called just look into the dictionary to see which context object is related. If you don't want to use the whole Alert object as a key, you could use just the address of the UIAlertView object:
NSString *alertKey = [NSString stringWithFormat:#"%x", baseAlert];
The address should be constant on the phone. Or you could tag each alert as the other poster suggested and use the tag to look up a context in the map.
Don't forget to clear out the context object when you are done!
A complete implementation that allows you to pass context:
#interface TDAlertView : UIAlertView
#property (nonatomic, strong) id context;
#end
#implementation TDAlertView
#end
And a usage example, note how we pre-cast the pointer:
#implementation SomeAlertViewDelegate
- (void)alertView:(TDAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSLog(#"%#", [alertView context]);
}
#end
you can also use the tag property (since it's a UIView subclass). This is just an int, but may be enough for you.
Instead of debating the meaning of "does not support subclassing", I'll provide a better answer. I created a generic contextInfo category for my job a couple months ago. I just put it on github: JLTContextInfo.
#import "objc/runtime.h"
#interface NSObject (JLTContextInfo)
- (NSMutableDictionary *)JLT_contextInfo;
#end
#implementation NSObject (JLTContextInfo)
- (NSMutableDictionary *)JLT_contextInfo
{
static char key;
NSMutableDictionary *contextInfo = objc_getAssociatedObject(self, &key);
if (!contextInfo) {
contextInfo = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &key, contextInfo, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return contextInfo;
}
#end
This creates a place to easily store extra data for any object derived from NSObject. Now the answer is nearly identical to the original question.
-(void) showAlert: (id) ctx {
UIAlertView *baseAlert = [[UIAlertView alloc]
initWithTitle: title
message: msg
delegate:self
cancelButtonTitle: cancelButtonTitle
otherButtonTitles: buttonTitle1, buttonTitle2, nil];
[[baseAlert JLT_contextInfo] setObject:ctx forKey:#"ctx"];
[baseAlert show];
[baseAlert release];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
id context = [[alertView JLT_contextInfo] objectForKey:#"ctx"];
[self performSelectorOnMainThread:#selector(xxx:) withObject: context waitUntilDone: NO];
}
}
Subclassing UIAlertView is not a good idea.
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIAlertView_Class/UIAlertView/UIAlertView.html
Subclassing Notes
The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
UIAlertView with a user supplied context and [self autorelease] answers this question in a different way.
I did a mix between Kendall's answer and the uses of blocks in one of my base view controller classes. Now I can use AlertView and ActionSheets with blocks which improves greatly readability. Here is how I did it :
In the .h of my ViewController I declare a block type (optional but recommanded)
typedef void (^AlertViewBlock)(UIAlertView*,NSInteger);
Also I declare a mutable dictionnary that will store the blocks for each alertview :
NSMutableDictionary* m_AlertViewContext;
In the implementation file I add a method to create the AlertView and save the block :
-(void)displayAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle withBlock:(AlertViewBlock)execBlock otherButtonTitles:(NSArray *)otherButtonTitles
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles: nil];
for (NSString* otherButtonTitle in otherButtonTitles) {
[alert addButtonWithTitle:otherButtonTitle];
}
AlertViewBlock blockCopy = Block_copy(execBlock);
[m_AlertViewContext setObject:blockCopy forKey:[NSString stringWithFormat:#"%p",alert]];
Block_release(blockCopy);
[alert show];
[alert release];
}
Note that I receive the same attributes as the constructor of the UIAlertView but the delegate (which will be self). Also I receive a AlertViewBlock object that I save in the m_AlertViewContext mutable dictionnary.
Then I show the alert as I would usually do.
In the delegate callbacks, I call the block and give it the parameters :
#pragma mark -
#pragma mark UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString* blockKey = [NSString stringWithFormat:#"%p",alertView];
AlertViewBlock block = [m_AlertViewContext objectForKey:blockKey];
block(alertView,buttonIndex);
[m_AlertViewContext removeObjectForKey:blockKey];
}
- (void)alertViewCancel:(UIAlertView *)alertView {
NSString* blockKey = [NSString stringWithFormat:#"%p",alertView];
[m_AlertViewContext removeObjectForKey:blockKey];
}
Now, whenever I need to use an AlertView I can call it like this :
[self displayAlertViewWithTitle:#"Title"
message:#"msg"
cancelButtonTitle:#"Cancel"
withBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
if ([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:#"DO ACTION"]){
[self doWhatYouHaveToDo];
}
} otherButtonTitles:[NSArray arrayWithObject:#"DO ACTION"]];
I did the same for the ActionSheet and now it's really easy to use those.
Hope it helps.
From my other answer, here's a quick and clean solution that takes advantage of associated objects. I mention in my other answer that you could even replace UIAlertView with NSObject and effectively add a context property to any object:
#import <objc/runtime.h>
#interface UIAlertView (Private)
#property (nonatomic, strong) id context;
#end
#implementation UIAlertView (Private)
#dynamic context;
-(void)setContext:(id)context {
objc_setAssociatedObject(self, #selector(context), context, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(id)context {
return objc_getAssociatedObject(self, #selector(context));
}
#end
And then you'll be able to do something like:
NSObject *myObject = [NSObject new];
UIAlertView *alertView = ...
alertView.context = myObject;
IMPORTANT:
And don't forget to nil the context in dealloc!!
You could subclass UIAlertView and add the property there.