Block for UIAlertViewDelegate - iphone

I'm pretty new to objective C and I'm just trying to figure out if I can use a block or a selector as the UIAlertViewDelegate argument for UIAlertView - and which is more appropriate?
I've tried the following but it just isn't working so I'm not sure if I'm on the right track or not?
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Checked In"
message:responseString
delegate:^(UIAlertView * alertView, NSInteger buttonIndex)
{
NSLog(#"Done!");
}
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
Thanks!

Great idea. Here it is. Just like alert view, except adds a block property that's invoked when the alert is dismissed. (Edit - I've simplified this code since the original answer. Here's what I use now in projects)
// AlertView.h
//
#import <UIKit/UIKit.h>
#interface AlertView : UIAlertView
#property (copy, nonatomic) void (^completion)(BOOL, NSInteger);
- (id)initWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles;
#end
//
// AlertView.m
#import "AlertView.h"
#interface AlertView () <UIAlertViewDelegate>
#end
#implementation AlertView
- (id)initWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles {
self = [self initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];
if (self) {
for (NSString *buttonTitle in otherButtonTitles) {
[self addButtonWithTitle:buttonTitle];
}
}
return self;
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (self.completion) {
self.completion(buttonIndex==self.cancelButtonIndex, buttonIndex);
self.completion = nil;
}
}
#end
You can extend this idea to supply blocks for other delegate methods, but the didDismiss is the most common.
Call it like this:
AlertView *alert = [[AlertView alloc] initWithTitle:#"Really Delete" message:#"Do you really want to delete everything?" cancelButtonTitle:#"Nevermind" otherButtonTitles:#[#"Yes"]];
alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
if (!cancelled) {
[self deleteEverything];
}
};
[alert show];

One must use UIAlertController for that approach as Apple document says
A UIAlertController object displays an alert message to the user. This
class replaces the UIActionSheet and UIAlertView classes for
displaying alerts. After configuring the alert controller with the
actions and style you want, present it using the
presentViewController:animated:completion: method.
In addition to displaying a message to a user, you can associate
actions with your alert controller to give the user a way to respond.
For each action you add using the addAction: method, the alert
controller configures a button with the action details. When the user
taps that action, the alert controller executes the block you provided
when creating the action object.
Apple Docs.
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"My Alert"
message:#"This is an alert."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];

Check out this UIAlertView-Blocks category on github. I use this and it works well.

This is an update to danh's implementation, which is incomplete because it is not possible to add multiple buttons. Passing a va_list to a function is a little tricky :-)
So you could do this, in order to be able to add multiple buttons to the UIAlertView:
- (id)initWithTitle:(NSString *)title message:(NSString *)message completion:(void (^)(NSInteger buttonIndex))completion cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
self = [super initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil ];
if(self){
_completion = completion;
va_list _arguments;
va_start(_arguments, otherButtonTitles);
for (NSString *key = otherButtonTitles; key != nil; key = (__bridge NSString *)va_arg(_arguments, void *)) {
[self addButtonWithTitle:key];
}
va_end(_arguments);
}
return self;
}
Update: There might be a better way of passing the va_list to super. I would like to mention that to me va_lists have something mystic to them :-)

Awesome idea. I have just completed exactly your idea using Category pattern, no subclass, call directly UIAlertView. Please follow these steps:
Category UIAlertView in .h file
#interface UIAlertView (AlertWithBlock)
- (void (^)(UIAlertView *alertView, NSInteger buttonIndex))delegateBlock;
- (void)setDelegateBlock:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))delegateBlock;
+ (id)alertWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles delegate:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))delegate;
#end
In .m file
#implementation UIAlertView(AlertWithBlock)
static char const *delegateBlockTagKey = "delegateBlockTagKey";
- (void (^)(UIAlertView *alertView, NSInteger buttonIndex))delegateBlock
{
return objc_getAssociatedObject(self, delegateBlockTagKey);
}
- (void)setDelegateBlock:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))delegateBlock
{
objc_setAssociatedObject(self, delegateBlockTagKey, delegateBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (id)alertWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles delegate:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))delegate
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];
alert.delegate = alert;
alert.delegateBlock = [delegate copy];
for (NSString *buttonTitle in otherButtonTitles)
{
[alert addButtonWithTitle:buttonTitle];
}
[alert show];
return alert;
}
#pragma mark - Delegate
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.delegateBlock)
{
alertView.delegateBlock(alertView, buttonIndex);
}
}
#end
Call like this
[UIAlertView alertWithTitle:#"Title" message:#"This is a message" cancelButtonTitle:#"Cancel" otherButtonTitles:#[#"Yes",#"No"] delegate:^(UIAlertView *alertView, NSInteger buttonIndex) {
NSLog(#"Click at button index %ld", buttonIndex);
}];
Hope it help.

I have written a simple extension in Swift, hope it helpful
import UIKit
extension UIAlertView {
func show(completion: (alertView: UIAlertView, buttonIndex: Int) -> Void){
self.delegate = AlertViewDelegate(completion: completion)
self.show()
}
class func showInput(title: String?, message: String?, cancellable: Bool, completion: (text: String?) -> Void){
var strOK = NSLocalizedString("OK",comment: "OK")
var strCancel = NSLocalizedString("Cancel",comment: "Cancel")
var alert = UIAlertView(title: title, message: message, delegate: nil, cancelButtonTitle: cancellable ? strCancel : strOK)
alert.alertViewStyle = UIAlertViewStyle.PlainTextInput
if(cancellable) {
alert.addButtonWithTitle(strOK)
}
alert.show { (alertView, buttonIndex) -> Void in
if(cancellable && alertView.cancelButtonIndex == buttonIndex) {
completion(text: nil)
return
}
completion(text: alertView.textFieldAtIndex(0)?.text)
}
}
private class AlertViewDelegate : NSObject, UIAlertViewDelegate {
var completion : (alertView: UIAlertView, buttonIndex: Int) -> Void
var retainedSelf : NSObject?
init(completion: (UIAlertView, Int) -> Void ) {
self.completion = completion
super.init()
self.retainedSelf = self
}
func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) {
var retain = self
retain.retainedSelf = nil
retain.completion(alertView: alertView, buttonIndex: buttonIndex)
}
}
}

Here is my implementation, seems like it's similar to most answers here:
http://stavash.wordpress.com/2013/01/31/quick-tip-uialertview-with-a-block-callback/

Simply use REKit. It's similar to BlocksKit, but it's more powerful.

Check out UIAlertView-Blocks category on Github.
I wrote this and is very easy to use and works well.
Good luck.

Here is another useful library to do the same. http://bit.ly/ichalrtvw
Code here: http://bit.ly/ichalertview

I had to edit the calling example a bit to stop complier error. Just a small tweak and xcode was happy.
UIAlertViewBlock *alert = [[UIAlertViewBlock alloc] initWithTitle:#"hi"
message:#"hi there"
completion:^(BOOL canceled,NSInteger buttonIndex) {
NSLog(#"canceled=%d", canceled);
NSLog(#"pressed=%d", buttonIndex);
}
cancelButtonTitle:#"cancel"
otherButtonTitles:#"ok", nil];
[alert show];

Related

How do you pass a variable to the UIAlertView delegate?

How do you pass a variable to the UIAlertView delegate?
I have a variable that I want to use in the alert view delegate. It is only used in the function that shows the UIAlertView and the UIAlertView delegate, so i don't think it should be a property on the controller. Is there a way to attach the variable to UIAlertView and retrieve it in the delegate?
- (void) someUserCondition:(SOCode *)userCode {
if ([userCode warrentsConfirmation] > 0) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Are you sure?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK",nil];
[alert setAlertViewStyle:UIAlertViewStyleDefault];
//TODO somehow store the code variable on the alert view
[alert show];
}
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if ([title isEqualToString:#"OK"]){
SOCode *userCode = //TODO somehow get the code from the alert view
[self continueWithCode:code];
}
}
in .h before interface:
extern const char MyConstantKey;
#interface ViewController...
in .m import:
import <objc/runtime.h>
in .m before implementation
const char MyConstantKey;
in .m implementation
-(void)viewDidAppear:(BOOL)animated{ //or wherever
NSString *aString = #"This is a string";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Testing" message:#"test is test" delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil];
[alert show];
[alert release];
objc_setAssociatedObject(alert, &MyConstantKey, aString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
in .m alertview callback
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
NSString *associatedString = objc_getAssociatedObject(alertView, &MyConstantKey);
NSLog(#"associated string: %#", associatedString);
}
Use Associated Objects. It is described in more detail here: Your New Friends: Obj-C Associated Objects
To set the object you use use:
objc_setAssociatedObject(alert, &key, userCode, OBJC_ASSOCIATION_RETAIN);
And then to get it back:
SOCode *userCode = objc_getAssociatedObject(alertView, &key);
You also need to add static char key; so that it is in the scope of moth methods.
Update
I have wrapped this into a category on UIAlertView. You can use Cocoapods to bring it in:
pod 'HCViews/UIAlertViewHCContext', '~> 1.2'
The source is available here: https://github.com/hypercrypt/HCViews/blob/master/Categories/UIAlertView%2BHCContext.h
A lot of posts talk about the concepts behind associated objects (which is good!) but sometimes you just want to see the code. Here's a clean and quick category that you can either put in a separate file or above the interface of one of your existing .m files (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!!
UIAlertView is a subclass of UIView which has a tag property you can set to an integer. Unfortunately if you need something other than an integer to identify/pass info to the delegate than you will need to set some properties (or set up an array with the tag indexing into it) on the delegate itself. Advaith's way will probably work but is technically not supported by Apple.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Are you sure?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK",nil];
[alert setAlertViewStyle:UIAlertViewStyleDefault];
alert.tag = SOMEINTEGER;
[alert show];
I suspect the most straight-forward way is a property in the alert view's delegate class. An alert view doesn't have any provision for "user info" and doesn't support sub-classing, which removes the only shortcuts that come to mind.
Subclass UIAlertView, add a property called userInfo with type of your choice. Set the user info value at the time you create an instance of Subclassed UIAlertView, and retrieve it from within the delegate method. (There you will get the subclassed instance which holds the userInfo)

is there easy way to handle UIAlertView result without delegation?

I have a function that shows a UIAlertView with YES/NO buttons, and it is used only inside the function's scope so I dont want to implement a delegation to catch the user feedback.
Is there any way to know what button users clicked without implement UIAlertViewDelegate, something like:
[alert show];
if([alert indexOfClickedButton] == indexOfYes)
{
....
}
Or lambda expression as in Animation
There is no way to avoid delegation completely, but you could create a wrapper to that effect along these lines:
#interface MyAlertViewDelegate : NSObject<UIAlertViewDelegate>
typedef void (^AlertViewCompletionBlock)(NSInteger buttonIndex);
#property (strong,nonatomic) AlertViewCompletionBlock callback;
+ (void)showAlertView:(UIAlertView *)alertView withCallback:(AlertViewCompletionBlock)callback;
#end
#implementation MyAlertViewDelegate
#synthesize callback;
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
callback(buttonIndex);
}
+ (void)showAlertView:(UIAlertView *)alertView
withCallback:(AlertViewCompletionBlock)callback {
__block MyAlertViewDelegate *delegate = [[MyAlertViewDelegate alloc] init];
alertView.delegate = delegate;
delegate.callback = ^(NSInteger buttonIndex) {
callback(buttonIndex);
alertView.delegate = nil;
delegate = nil;
};
[alertView show];
}
#end
(ARC is assumed, if you are not using it change delegate = nil to [delegate release].)
Usage would be something like:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Confirm" message:#"Yes or No?" delegate:nil cancelButtonTitle:#"Cancel" otherButtonTitles:#"Yes",#"No", nil];
[MyAlertViewDelegate showAlertView:alert withCallback:^(NSInteger buttonIndex) {
// code to take action depending on the value of buttonIndex
}];
I have written a blog post about how to (and why) add block callbacks to alert views, action sheets and animations:
http://blog.innovattic.com/uikitblocks/
If you just want a working implementation of this you can download the sources files from GitHub:
https://github.com/Innovattic/UIKit-Blocks
Usage:
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"My easy alert"
message:#"Would you like to perform some kind of action?"
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[alert setHandler:^(UIAlertView* alert, NSInteger buttonIndex) {
NSLog(#"Perform some kind of action");
} forButtonAtIndex:[alert firstOtherButtonIndex]];
[alert show];
It's very easy. Say you have an alert, something like this:
//Alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Confirm" message:#"Yes or No?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Yes",#"No", nil];
[alert show];
You're going to need to add this method:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
A possible implementation of this method would look like this:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
//Checks For Approval
if (buttonIndex == 1) {
//do something because they selected button one, yes
} else {
//do nothing because they selected no
}
}
You can do this using custom view which can be hidden and shown to avoid ActionSheets
UIView *AlertVw=[UIView alloc]initWithFrame:CGRect(x,y,w,h)]];
UIButton *SaveButton=[UIButton alloc]initWithFrame:CGRect(x,y,w,h)]];
[CustomButton setTitle:#"Ok" forState:UIControlStateNormal];
[SaveButton addTarget:self action:#selector(SaveClicked) forControlEvents:UIControlEventTouchUpInside];
UIButton *CancelButton=[UIButton alloc]initWithFrame:CGRect(x,y,w,h)]];
[CustomButton setTitle:#"Cancel" forState:UIControlStateNormal];
[CancelButton addTarget:self action:#selector(CancelClicked) forControlEvents:UIControlEventTouchUpInside];
[AlertVw addSubview:SaveButton];
[AlertVw addSubview:CancelButton];
[self.view addSubview:AlertVw];
-(void)SaveButton
{
//Code to apply on Save clicked
[AlertVw removeFromSuperView]; //Also you can use AlertView.hidden=YES;
}
-(void)CancelButton
{
//Code to apply on cancel clicked
[AlertVw removeFromSuperView]; //Also you can use AlertView.hidden=YES;
}
No need to derive the class. With Block, it is easy to get user selected button index.
typedef void(^AlertViewCallBackBlock)(NSInteger selectedIndex);
#interface ABC ()
#property (nonatomic, copy) AlertViewCallBackBlock alertViewBlock;
#end
#implementation
- (void)showAlert {
self.alertViewBlock = ^(NSInteger selectedIndex) {
if (selectedIndex == 1) {
}
};
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Confirm" message:#"Yes or No?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Yes",#"No", nil];
[alert show];
}
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
self.alertViewBlock(buttonIndex);
}
#end
Thanks Arkku. Here is the Swift version.
https://github.com/exchangegroup/alert-view-with-callback-swift
let alertView = UIAlertView(...)
AlertViewWithCallback().show(alertView) { alertView, buttonIndex in
println("You closed alert by tapping button #\(buttonIndex)")
}
UIAlertView is deprecated from iOS 8.0, a better solution will be using UIAlertController:
let alert = UIAlertController(title: "message", message: "Title", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "YES", style: .Default, handler: { (action) -> Void in
// Action for YES
}))
alert.addAction(UIAlertAction(title: "NO", style: .Default, handler: { (action) -> Void in
// Action for NO
}))
self.view.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil)

Multiple UIActionSheets on the same delegate

I'm writing a puzzle game. When the user presses the check button, I see if the solution they entered is correct. Depending on the result, I present one of two action sheets for them. For now I just have some NSLog statements to make sure things are getting called, but only one of the sheets seems to work properly.
Nothing gets called when I click a button in showErrorsActionSheet. The action sheet disappears off the screen, but the logs never print.
I suspect it has something to do with having two actionsheets declared to the same delegate (self)
- (void) checkSolution {
//code determines the value of the BOOL allCorrect
if (allCorrect) { //IF ALL OF THE LETTERS WERE CORRECT
//display UIAlertView;
NSLog(#"allCorrect");
UIActionSheet *levelCompleteActionSheet = [[UIActionSheet alloc] initWithTitle:#"Congratulations! You Have Finished the Level!" delegate:self cancelButtonTitle:#"Review my work" destructiveButtonTitle:#"Choose next puzzle" otherButtonTitles:nil, nil];
[levelCompleteActionSheet showInView:self.view];
[levelCompleteActionSheet release];
}
else {
//[self showIncorrectLettersInRed];
UIActionSheet *showErrorsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Sorry, thats not right. Show errors in red?" delegate:self cancelButtonTitle:#"No Thanks, I'll keep trying" destructiveButtonTitle:#"Yes please, I'm stuck!" otherButtonTitles:nil, nil];
[showErrorsActionSheet showInView:self.view];
[showErrorsActionSheet release];
}
}
the methods that are supposed to be called are:
- (void) levelCompleteActionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex != [actionSheet cancelButtonIndex]) {
NSLog(#"return to levelSelect");
//pushViewController:levelSelect
}
else {
NSLog(#"continue to examine solution");
}
}
- (void) showErrorsActionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex != [actionSheet cancelButtonIndex]) {
NSLog(#"show errors in red");
}
else {
NSLog(#"continue to try");
}
}
and Ive declared the UIActionSheet protocol in the interface file as follows:
#interface GamePlay : UIViewController <UIActionSheetDelegate> {
Set a tag for each actionSheet, then use a switch statement in the UIActionSheet delegate.
Assign a tag
- (void)checkSolution
{
if (allCorrect)
{
UIActionSheet *levelCompleteActionSheet = [[UIActionSheet alloc] initWithTitle:#"Congratulations! You Have Finished the Level!" delegate:self cancelButtonTitle:#"Review my work" destructiveButtonTitle:#"Choose next puzzle" otherButtonTitles:nil, nil];
[levelCompleteActionSheet setTag: 0];
[levelCompleteActionSheet showInView:self.view];
[levelCompleteActionSheet release];
}
else
{
UIActionSheet *showErrorsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Sorry, thats not right. Show errors in red?" delegate:self cancelButtonTitle:#"No Thanks, I'll keep trying" destructiveButtonTitle:#"Yes please, I'm stuck!" otherButtonTitles:nil, nil];
[showErrorsActionSheet setTag: 1];
[showErrorsActionSheet showInView:self.view];
[showErrorsActionSheet release];
}
}
UIActionSheet Delegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch ( actionSheet.tag )
{
case 0: /* levelCompleteActionSheet */
{
switch ( buttonIndex )
{
case 0: /* 1st button*/
break;
case 1: /* 2nd button */
break;
}
}
break;
case 1: /* showErrorsActionSheet */
break;
}
}
The same would apply anywhere else in this class as well, including levelCompleteActionSheet: and showErrorsActionSheet:. The only difference is, you would need to create an iVar for each actionSheet instead of creating them in checkSolution.
The methods that will be called by a UIActionSheet on its delegate are the methods listed in the UIActionSheetDelegate protocol.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIModalViewDelegate_Protocol/UIActionSheetDelegate/UIActionSheetDelegate.html
To be called, your method must be one of those methods. I don't see levelCompleteActionSheet or showErrorsActionSheet listed in that protocol! :) Your method must be named actionSheet:clickedButtonAtIndex:, and not some name you make up out of whole cloth.
Using Tag solve this problem
levelCompleteActionSheet.tag = 100;
showErrorsActionSheet.tag = 101;
- (void) levelCompleteActionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if(actionSheet.tag == 100){
// levelCompleteActionSheet implement your required function
}
else if(actionSheet.tag == 101){
// showErrorsActionSheet implement your required function
}
}

clickedButtonAtIndex in appdelegate is not called

I am calling UIAlert with 2 buttons "Cancel" and "OK" in MyapplicationAppDelegate.m file , the alert is called but on tap of "Cancel" or "OK" button
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
method is not called.
I have added UIAlertViewDelegate in the MyapplicationAppDelegate.h file as below
#import UIKit/UIKit.h
#interface MyapplicationAppDelegate: NSObject UIApplicationDelegate,UIAlertViewDelegate
{
..
}
I want to know what else is required.
I am not sure whether its your typo while posting the question but you should call the delegate within <.. , .. >
#interface MyapplicationAppDelegate: NSObject <UIApplicationDelegate,UIAlertViewDelegate> { .. }
Here is sample code for UIAlertView
UIAlertView *myAlertView = [[UIAlertView alloc]initWithTitle:#""message:#"Your message goes here" delegate:self cancelButtonTitle:#"OK"otherButtonTitles:nil];
[myAlertView show];
[myAlertView release];
I am not sure myAlertView.delgate = self is the right method but i do set delegate with initWithTitle
For me,
#define appDelegate ((AppDelegate*)[UIApplication sharedApplication].delegate)
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil
message:#"Say Yes or NO?" delegate:appDelegate
cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
alertView.tag = 1;
[alertView show];
[alertView release];
do the trick!
Now its going into, -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex; in AppDelegate file without adding,
#interface MyapplicationAppDelegate: NSObject <UIApplicationDelegate,UIAlertViewDelegate> { .. }

iphone navigationController : wait for uialertview response before to quit the current view

I have a view with a back button managed with a navigation controller and I want to check if a file has been saved when the user click on the back button.
If the file has been saved you go back in the previous view, else a uialertview ask you if you want to save the file or not.
So I did that but the view disapear and the alertview appear after.
-(void)viewWillDisappear:(BOOL)animated {
if(!self.fileSaved){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Save the file?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[alert show];
[alert release];
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
switch (buttonIndex) {
case 0:
NSLog(#"NO");
break;
case 1:
NSLog(#"yes");
break;
default:
break;
}
}
When viewWillDisappear is called, it's already too late. You should intercept the back button earlier on. I have never done it, but my suggestion is to set the delegate on the navigationBar property in your viewDidAppear method:
// save the previous delegate (create an ivar for that)
prevNavigationBarDelegate = self.navigationController.navigationBar.delegate;
self.navigationController.navigationBar.delegate = self;
Don't forget to set it back in viewWillDisappear:
self.navigationController.navigationBar.delegate = prevNavigationBarDelegate;
Then intercept the shouldPopItem method:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
if(!self.fileSaved) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Save the file?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[alert show];
[alert release];
return NO;
}
if ([prevNavigationBarDelegate respondsToSelector:#selector(navigationBar:shouldPopItem:)])
return [prevNavigationBarDelegate navigationBar:navigationBar shouldPopItem:item];
return YES;
}
And in the YES handler for the dialog, manually pop the controller:
[self.navigationController popViewController:YES];
You must subclass UINavigationController for this to work. Then override - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item .
You should set up a custom Delegate protocol that your view controllers adopt and, if you allow it to pop, call your [super navigationBar shouldPopItem:], else, return NO to the above method.
Wouldn't it be easier just to add a left button item as in the following:
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(saveThisDate)];
self.navigationItem.leftBarButtonItem = backButton;
[backButton release];
To follow up on nobre response and as Jon mentionned it, the best way is to subclass UINavigationController.
The easiest way and fastest way to acheive this :
Modify the class of your navigation controller in Interface Builder to inherit from CustomNavigationControllerDelegate
Implement the CustomNavigationControllerDelegate protocol in your UIViewController
#interface YourViewController <CustomNavigationControllerDelegate>
#pragma mark - UINavigationBar Delegate Methods
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancel otherButtonTitles:ok, nil];
alert.tag = kpopup_back;
[alert show];
return NO;
}
Register your controller as the delegate
#pragma mark - viewWillAppear
- (void) viewWillAppear:(BOOL)animated
{
((CustomNavigationController*)self.navigationController).customDelegate = self;
}
Finally and important part, REMOVE the delegate (to avoid to re-trigger yourself on the pop) and pop the controller yourself in the the UIAlertViewDelegate
case kpopup_back :
{
if(buttonIndex != 0) //OK
{
((CustomNavigationController*)self.navigationController).customDelegate = nil;
[self.navigationController popViewControllerAnimated:YES];
}
}
break;
It works flawlessly on my side, hope it can help.
Here are the sources :
CustomNavigationControllerDelegate.h
#import <UIKit/UIKit.h>
#protocol CustomNavigationControllerDelegate <NSObject>
#optional
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
#end
#interface CustomNavigationController : UINavigationController
#property (nonatomic, retain) id<CustomNavigationControllerDelegate> customDelegate;
#end
CustomNavigationControllerDelegate.m
#import "CustomNavigationController.h"
#interface CustomNavigationController ()
#end
#implementation CustomNavigationController
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
if (_customDelegate && [_customDelegate respondsToSelector:#selector(navigationBar:shouldPopItem:)]) {
return [_customDelegate navigationBar:navigationBar shouldPopItem:item];
}
return YES;
}
#end