Releasing a UIAlertViewDelagate - iphone

I have a view controller that need to handle a number of UIAlertViews, so ideally I don't want to set the delegate to self, and handle everything with tags on the alert view - unless that is the preferred approach.
If I create a class implementing UIAlertViewDelegate, where/how can I safely release it?
-(void)myMethod {
MyUIAlertViewDelegate *myDelegate = [[MyUIAlertViewDelegate alloc] init];
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle ... delegate:myDelegate ...
[myAlertView show];
[myAlertView release];
}
What recommendations are there for creating this type of custom delegate?

I’m unsure what you stand to gain out of this. Are you creating a new instance of the class for each alert view? That seems like unnecessary overhead to me. Using tags doesn’t have to be messy, though; just use the delegate methods to call into other methods that do what you want:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch ([alertView tag]) {
case kWidgetATag:
[self doSomethingForWidgetA];
case kWidgetBTag:
[self doSomethingForWidgetB];
}
}

I think the best way to release your view controller would be in method that is called the last:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self release];
}

It seems strange to have a class that exists solely to handle the UIAlertView's methods.
Typically some view controller will have direct interest in the result of the UIAlertView's result. That view controller will implement the UIAlertViewDelegate protocol, create and present the UIAlertView (setting delegate to self), and subsequently receive its delegate messages.
The ViewController will then update View or Model concerns as needed.

Related

Calling UIAlertView clickedbutton at index programmatically

I am trying to call UIAlertView's delegate method programmatically. Here is the code:
if([vc respondsToSelector:#selector(alertView:clickedButtonAtIndex:)]) {
// Manually invoke the alert view button handler
[(id <UIAlertViewDelegate>)vc alertView:nil
clickedButtonAtIndex:0];
}
It works fine on iOS5.0 but is not working on iOS6.0 and comments or suggestions are most welcomed :)
Here is the complete method for detail:
TWTweetComposeViewController *vc = [[[TWTweetComposeViewController alloc] init]autorelease];
// Settin The Initial Text
[vc setInitialText:status];
[vc setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
if(result == TWTweetComposeViewControllerResultDone) {
NSLog(#"Tweeted Sucessfully");
}
}];
if([delegate isKindOfClass:[UIViewController class]]){
[(UIViewController *)delegate presentModalViewController:vc animated:YES];
}
//alertView:clickedButtonAtIndex:
if([vc respondsToSelector:#selector(alertView:clickedButtonAtIndex:)]) {
// Manually invoke the alert view button handler
[(id <UIAlertViewDelegate>)vc alertView:nil
clickedButtonAtIndex:0];
}
}
in you code just give the alertview with your alertview obect name like bellow..
[(id <UIAlertViewDelegate>)vc alertView:yourAlertView
clickedButtonAtIndex:0];
otherwise Just try with this bellow code..
id<UIAlertViewDelegate> delegate = yourAlertView.delegate;
yourAlertView.delegate = nil;
[delegate alertView:yourAlertView clickedButtonAtIndex:0];
see this link for some other option about it..
why-doesnt-dismisswithclickedbuttonindex-ever-call-clickedbuttonatindex
It is bad practice to directly call delegate methods. UIAlertView has a method called dismissWithClickedButtonIndex:animated:. If you call that, the UIAlertViewDelegate methods alertView:willDismissWithButtonIndex: and
alertView:didDismissWithButtonIndex: will be called, assuming your delegate is set correctly.
You can use this delegate this will work for you..
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex;
There are no such differences regarding implementation of Alert view in iOS 6. You can complete your task easily by using this delegate method - :
(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex;
try this and after that let us know what kind of warning you get in console...
TWTeetComposeViewController deprecated in IOS6. Please try with DETweet instead. :) Works fine on iOS 6 too. :)

A little bit complicated releasing issue for my custom alert view having UITextField

I know the ARC in iOS 5 but I'm now developing pre-iOS 5 code style, and want to solve this problem by a manual release approach.
My only goal for this is to make a very handy custom alert view with UITextField.
I have a 'BigView' view that has many functions in it. And it can possibly generate many UIAlertView for many different situation on the display with that view. So I know the way use UIAlertViewDelegate for each alert view, but kind of experimentally try want to make this as like UIButton's 'addTarget'(actually it's UIControl's method).
Briefly,
This is in the part of 'BigView' class and my 'TextAlert' instance fired by a button for email gathering .
BigView.m
- (void)emailFeedback:(id)sender
{
TextAlert *textAlert = [[TextAlert alloc] initWithTitle:#"Enter your email address"];
[textAlert setTarget:self action:#selector(textAlertInputed:)];
// [textAlert release];
}
- (void)textAlertInputed:(NSString *)text
{
NSLog(#"text alert inputed, text: %#", text);
}
and these are full my TextAlert files.
TextAlert.h
#import <Foundation/Foundation.h>
#interface TextAlert : NSObject <UIAlertViewDelegate>
{
UIAlertView *alertView;
UITextField *textField;
id target;
SEL action;
}
- (id)initWithTitle:(NSString *)title;
- (void)setTarget:(id)target action:(SEL)action;
#end
TextAlert.m
#import "TextAlert.h"
#implementation TextAlert
- (id)initWithTitle:(NSString *)title
{
if (self = [super init])
{
alertView = [[UIAlertView alloc] initWithTitle:title message:#"beneath" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
textField = [[UITextField alloc] initWithFrame:CGRectMake(12, 45, 260, 25)];
CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0, 60);
[alertView setTransform:myTransform];
[textField setBackgroundColor:[UIColor whiteColor]];
[alertView addSubview:textField];
[alertView show];
}
return self;
}
- (void)dealloc
{
[alertView release]; alertView = nil;
[textField release]; textField = nil;
[super dealloc];
}
- (void)setTarget:(id)_target action:(SEL)_action
{
target = _target;
action = _action;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[target performSelector:action withObject:textField.text];
}
#end
So my main problem is the releasing point of TextAlert instance in the 'BigView' as you can see the only comment part full codes above. Of course if I remove that comment out, I got crash for call for method of deallocated.
And I also get error make textAlert instance as autoreleased one.
For me, the only solution for this is to make the 'textAlert' object in the 'BigView' a member of 'BigView' not local object. But in that case, my initial goal for handy and lightweight approach for this is not satisfied, I think. And the 'BigView' has already many member instances so I don't want to add any more.
So any suggestions? Or It will be welcome any comment for this trying. I'm ready to hear any
reproves to my insufficient code, really.
Thanks in advance,
MK
If everything works except your release problem you should only consider implementing public "show" method and private "dismiss" method (in your custom alert view).. In show method you should call [self retain] beside other things and on dismiss (add this target to button or whatever dismisses your view) call [self relese].
This isn't directly what you asked for, but could help you anyway.
Handling multiple UIAlertViews in a single UIViewController can be painful. When I ran into this problem, I found an alternative control on github, called BlockAlertsAndActionSheets. It uses blocks instead of delegates, the appearance can be fully customized (even to the default Apple-style) and there is also an "AlertView with an UITextField". Works good for me and I didn't have to reinvent that wheel! ;-)

Crash when UIAlertView is clicked

When I click the UIAletView, I receive the following error.
alertView:clickedButtonAtIndex:]: message sent to deallocated instance 0x84c7010
This is the code I have used.
UIAlertView *testAlert = [[ UIAlertView alloc]initWithTitle:messageTitle message:messageBody delegate:self cancelButtonTitle:messageClose otherButtonTitles:messageTryAgain, nil];
testAlert.tag = 2;
[testAlert show];
[testAlert release];
And I have the delegate method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
}
When I click the UIAlertView , even before the control reaches the delegate method, the app crashes. What could be the reason. What am I doing wrong?
This is "one hack of a solution".
Hopefully it helps you understand that your delegate is the memory issue. The delegete (in this case self) is deallocated somehow before the testAlert is dismissed
// retain self to avoid crash you were experiencing earlier
UIAlertView *testAlert = [[ UIAlertView alloc]initWithTitle:messageTitle message:messageBody delegate:[self retain] cancelButtonTitle:messageClose otherButtonTitles:messageTryAgain, nil];
testAlert.tag = 2;
[testAlert show];
[testAlert release];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
// release self because you've gotten past the crash
[self release];
}
This is in no way an elegant solution and should encourage you to debug your app further to find out why self is being deallocated prematurely
Just wondering, could you show us your .h file?
If I had to hazard a guess, you've forgotten to set your class to respond to UIAlertViews as a delegate
You might be missing something like this:
#interface MyClass : UIViewController <UIAlertViewDelegate>
If ARC enable UIAlertView Object retain and no need to release, it's automatically release your object.

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 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;
}
}
}