My scenario is as follows
1) I created a Category for UIAlertView class
//UIAlertView+Remove.m file
#import "UIAlertView+Remove.h"
#implementation UIAlertView (Remove)
- (void) hide {
[self dismissWithClickedButtonIndex:0 animated:YES];
}
- (void)removeNotificationObserver
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSCalendarDayChangedNotification object:nil];
}
#end
2)Added a notification to UIAlertView object when its show
3)And I want to call removeNotificationObserver method when user
click on any button in alertview to remove notification observer.
My tried out scinerios,
Calling its from - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex delegate is not possible here because of delegate is not properly set to all alertview objects.
Called it from a -dealloc method in category but -dealloc is not triggering when alertview close
Can anybody help me to get through this?
UIAlertView is deprecated since iOS8 so I suggest you should not use it anymore instead of that you can use UIAlertController as below which can perform the action of buttons without the use of any delegate methods.
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle: #"Title" message:#"Message" preferredStyle: UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle: #"OK" style: UIAlertActionStyleDefault handler: ^(UIAlertAction *action)
{
}];
[alertController addAction: OKAction];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle: #"cancel" style: UIAlertActionStyleDefault handler: ^(UIAlertAction *action)
{
}];
[alertController addAction: cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
thank you for the responses!
Finally, I solved it myself by implementing SubClass for UIAlertView instead of using Category. Here I commented my code snippet, it may be helpful for those who experience same issue
//UIAlertView_AutoClose.m file
#import "UIAlertView_AutoClose.h"
#implementation UIAlertView_AutoClose
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
if(delegate == nil){
delegate = self;
}
return [super initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSCalendarDayChangedNotification object:nil];
NSLog(#"Reached alertview_autoclose");
}
- (void) hide {
[self dismissWithClickedButtonIndex:0 animated:YES];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSCalendarDayChangedNotification object:nil];
}
#end
I have created a signup form with a UIAlertController and used the method addTextFieldWithConfigurationHandler to add a text field. But there is a little problem.
When the form shows up, the keyboard and modal appear with a smooth animation. When closing the form, the modal disappears first, and then the keyboard disappears. This makes the keyboard make a sudden downward fall.
How can I make the modal and keyboard graciously disappear?
lazy var alertController: UIAlertController = { [weak self] in
let alert = UIAlertController(title: "Alert", message: "This is a demo alert", preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler { textField in
textField.delegate = self
}
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
return alert
}()
#IBAction func alert() {
presentViewController(alertController, animated: true, completion: nil)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
alertController.dismissViewControllerAnimated(true, completion: nil)
return true
}
You can set your view controller or other object as transitioning delegate of your UIAlertController (alert.transitioningDelegate) and make a custom animation for dismissing.
Code sample:
#interface ViewController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning, UITextFieldDelegate>
#property (assign, nonatomic) NSTimeInterval keyboardAnimationDuration;
#property (assign, nonatomic) CGFloat keyboardHeight;
#property (nonatomic, strong) UIAlertController *alertController;
#property (nonatomic,strong) id <UIViewControllerTransitioningDelegate> transitioningDelegateForAlertController;
#end
#implementation ViewController
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self subscribeForKeyboardNotification];
}
#pragma mark - Keyboard notifications
- (void)subscribeForKeyboardNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillAppear:)
name:UIKeyboardWillShowNotification
object:nil];
}
- (void)keyboardWillAppear:(NSNotification *)notification {
self.keyboardAnimationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.keyboardHeight = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
}
#pragma mark - IBAction
- (IBAction)showAlertButtonPressed:(id)sender {
[self showAlert];
}
- (void)showAlert {
self.alertController = [UIAlertController alertControllerWithTitle:#"Alert"
message:#"This is a demo alert"
preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(self) weakSelf = self;
[self.alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.delegate = weakSelf;
}];
self.transitioningDelegateForAlertController = self.alertController.transitioningDelegate;
self.alertController.transitioningDelegate = self;
[self.alertController addAction:[UIAlertAction actionWithTitle:#"Ok"
style:UIAlertActionStyleCancel
handler:nil]];
[self presentViewController:self.alertController animated:YES completion:nil];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[self.alertController dismissViewControllerAnimated:YES completion:nil];
return YES;
}
#pragma mark - UIViewControllerTransitioningDelegate
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
return [self.transitioningDelegateForAlertController animationControllerForPresentedController:presented
presentingController:presenting
sourceController:source];
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
return self;
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return self.keyboardAnimationDuration ?: 0.5;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
UIViewController *destination = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if ([destination isBeingPresented])
[self animatePresentation:transitionContext];
else
[self animateDismissal:transitionContext];
}
- (void)animatePresentation:(id <UIViewControllerContextTransitioning>)transitionContext {
NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = transitionContext.containerView;
fromController.view.frame = container.bounds;
toController.view.frame = container.bounds;
toController.view.alpha = 0.0f;
[container addSubview:toController.view];
[fromController beginAppearanceTransition:NO animated:YES];
[UIView animateWithDuration:transitionDuration
animations:^{
toController.view.alpha = 1.0;
}
completion:^(BOOL finished) {
[fromController endAppearanceTransition];
[transitionContext completeTransition:YES];
}];
}
- (void)animateDismissal:(id <UIViewControllerContextTransitioning>)transitionContext {
NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[toController beginAppearanceTransition:YES animated:YES];
[UIView animateWithDuration:transitionDuration
animations:^{
fromController.view.alpha = 0.0;
[fromController.view endEditing:YES];
CGRect frame = fromController.view.frame;
frame.origin.y += self.keyboardHeight / 2;
fromController.view.frame = frame;
}
completion:^(BOOL finished) {
[toController endAppearanceTransition];
[transitionContext completeTransition:YES];
}];
}
#end
Result:
P.S.: I used old alert's transitioning delegate for presentation because I can't reproduce an original animation. So animatePresentation: method is never used.
I had the exact same problem you had and found the solution incidentally. You probably don't need this anymore, but for the sake of others like me, here is the answer:
Swift:
override func canBecomeFirstResponder() -> Bool {
return true
}
Objective-C:
- (BOOL)canBecomeFirstResponder {
return true;
}
Just add this code in the view controller handling the alert. Only tested in swift.
Its pretty simple.
if your UIAlertController delegate are present in self View Controller. then you can do it in its delegate method for Dismiss AlertController. You can [youtTextField resignFirstResponder] in your UIAlertController object which have a button for dismiss it. (like OK or Cancel) so your presented KeyBoard will be dismissed.
I didn't tried it but It will work. but you have to handle textField and Alert correctly.
I assume the jumping down of the UIAlertController is if it dismisses after you press 'return' on the keyboard. If so, I have found a way for the Alert and keyboard to dismiss smoothly from a return action.
You will need declare the UIAlertController within the class file
#property (strong, nonatomic) UIAlertController *alertController;
And you will also need to use the UITextFieldDelegate with the viewController
When adding the textField to the UIAlertController this is where you will need to set the delegate of it to self. (weakSelf used as it is within a block)
#interface ViewController ()<UITextFieldDelegate>
Within the method you are auctioning the UIAlertController -
self.alertController = [UIAlertController alertControllerWithTitle:#"Alert" message:#"This is the message" preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(self) weakSelf = self;
[self.alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.delegate = weakSelf;
}];
[self presentViewController:self.alertController animated:YES completion:nil];
Add this UITextField delegate method which will fire once the return button has been pressed on the keyboard. This means you can action for the UIAlertController to dismiss just prior to the keyboard dismissing, thus it makes it all work smoothly.
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[self.alertController dismissViewControllerAnimated:YES completion:nil];
return YES;
}
I've tested this and should work exactly the way you require.
Thanks,
Jim
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[self.view endEditing:YES];
// or you can write [yourtextfield refignFirstResponder]
[alertView dismissWithClickedButtonIndex:buttonIndex animated:TRUE];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex==1) {
[[alertView textFieldAtIndex:0] resignFirstResponder];
} else {
[[alertView textFieldAtIndex:0] resignFirstResponder];
}
}
Use your button index (Ok or Cancel button index)
no need to do any thing you just have to implement this much of code, it works for me, no need to declare any kind of delegate methods
- (void)showAlert {
self.alertController = [UIAlertController alertControllerWithTitle:#"Alert"
message:#"Enter Name:"
preferredStyle:UIAlertControllerStyleAlert];
[self.alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
}];
[self.alertController addAction:[UIAlertAction actionWithTitle:#"Ok"
style:UIAlertActionStyleCancel
handler:nil]];
[self presentViewController:self.alertController animated:YES completion:nil];
}
Swizzle viewWillDisappear method for UIAlertController, and perform resignFirstResponder on correspodent text field or call endEditing: on controller's view
I am using for this ReactiveCocoa:
let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler {
textField in
}
let textField = alert.textFields!.first!
alert.rac_signalForSelector(#selector(viewWillDisappear(_:)))
.subscribeNext {
_ in
textField.resignFirstResponder()
}
I have an action sheet that when one of it's options are clicked successfully calls clickedButtonAtIndex when run in the simulator but when testing on an iPhone (5s in Xcode 6) it doesn't reach the callback.
The header...
#protocol SGETriggerToolBarDelegate
-(void)showCustomEditView;
#end
#interface SGETriggerToolBarController : UIViewController <UIActionSheetDelegate>
#property (nonatomic, assign) id <SGETriggerToolBarDelegate> delegate;
#property (nonatomic, strong) UIToolbar *toolbar;
in the implementation...
// in xController.m
// ...
- (void)triggerButtonHandler
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Select an event type"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
for (SGETrigger *trigger in triggers) {
[actionSheet addButtonWithTitle:trigger.name];
}
[actionSheet addButtonWithTitle:#"Cancel"];
actionSheet.cancelButtonIndex = triggers.count;
[actionSheet showFromToolbar:self.toolbar];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == [actionSheet cancelButtonIndex]) {
return;
} else {
selectedTrigger = triggers[buttonIndex];
triggerButton.title = [NSString stringWithFormat:#"• %# •", selectedTrigger.name];
[delegate showCustomEditView];
}
}
// ...
If you get "Presenting action sheet clipped by its superview. Some controls might not respond to touches"
Replacing...
[actionSheet showFromToolbar:self.toolbar];
with
[actionSheet showInView:[UIApplication sharedApplication].keyWindow];
solved this for me.
So for some reason after I've just added an actionsheet it's coming up and stopping above the bottom of the screen. Is there a way to manually tell this where to stop?
- (void)showActionSheet:(UIButton *)standardButton{
UIActionSheet *popupQuery = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:#"Cancel Button" destructiveButtonTitle:nil otherButtonTitles:#"Choose From Library", #"Take Photo", nil];
popupQuery.actionSheetStyle = UIActionSheetStyleAutomatic;
[popupQuery showInView:self];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
// Choose from library tapped
NSLog(#"Choose");
} else if (buttonIndex == 1) {
// Take a photo tapped
NSLog(#"Take");
}
}
Try self.view instead of self.
guys:
There is two buttons in my viewController of test app, the right one I call it "NO",
and the other one is "YES". The two buttons will call two different functions, and when
user press one of the buttons , I want show the user an alert to confirm that.
I know use the UIAlertViewDelegate
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
but there is two buttons, I am puzzled. How can I know which button is pressed.
So,pls help me with this, thank you in advance!
When you create an UIAlertView you can set up a tag for it
-(IBAction)yesButtonClick:(id)sender{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Message" delegate:self cancelButtonTitle: #"Cancel" otherButtonTitles:#"OK", nil];
alert.tag = 101;
[alert show];
}
-(IBAction)noButtonClick:(id)sender{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Message" delegate:self cancelButtonTitle: #"Cancel" otherButtonTitles:#"OK", nil];
alert.tag = 102;
[alert show];
}
In the delegate method check which alert is being shown
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView.tag == 101) {
// from YES button
}
else if (alertView.tag == 102) {
// from NO button
}
}
you can use the tag attribute to make the difference between your tow UIAlertView
in the function of button 1
alertView1.tag=1;
and in
-(void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(actionSheet.tag==1){
//first button was clicked
}
}
- (void)alertView:(UIAlertView *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex{
switch(buttonIndex){
case 0:
//YES button handler
break;
case 1:
//NO button handler
break;
default:
break;
}
}