iOS dismiss UIAlertView beforing showing another - ios

I have a Utils class which shows UIAlertView when certain notifications are triggered. Is there a way to dismiss any open UIAlertViews before showing a new one?
Currenty I am doing this when the app enters the background using
[self checkViews:application.windows];
on applicationDidEnterBackground
- (void)checkViews:(NSArray *)subviews {
Class AVClass = [UIAlertView class];
Class ASClass = [UIActionSheet class];
for (UIView * subview in subviews){
if ([subview isKindOfClass:AVClass]){
[(UIAlertView *)subview dismissWithClickedButtonIndex:[(UIAlertView *)subview cancelButtonIndex] animated:NO];
} else if ([subview isKindOfClass:ASClass]){
[(UIActionSheet *)subview dismissWithClickedButtonIndex:[(UIActionSheet *)subview cancelButtonIndex] animated:NO];
} else {
[self checkViews:subview.subviews];
}
}
}
This makes it easy on applicationDidEnterBackground as I can use application.windows
Can I use the AppDelegate or anything similar to get all the views, loop through them and dismiss any UIAlertViews?

for (UIWindow* window in [UIApplication sharedApplication].windows) {
NSArray* subviews = window.subviews;
if ([subviews count] > 0)
if ([[subviews objectAtIndex:0] isKindOfClass:[UIAlertView class]])
[(UIAlertView *)[subviews objectAtIndex:0] dismissWithClickedButtonIndex:[(UIAlertView *)[subviews objectAtIndex:0] cancelButtonIndex] animated:NO];
}

iOS6 compatible version:
for (UIWindow* w in UIApplication.sharedApplication.windows)
for (NSObject* o in w.subviews)
if ([o isKindOfClass:UIAlertView.class])
[(UIAlertView*)o dismissWithClickedButtonIndex:[(UIAlertView*)o cancelButtonIndex] animated:YES];

iOS7 compatible version:
I made a category interface that stores all instance in init method.
I know it's a very inefficient way.
#import <objc/runtime.h>
#import <objc/message.h>
#interface UIAlertView(EnumView)
+ (void)startInstanceMonitor;
+ (void)stopInstanceMonitor;
+ (void)dismissAll;
#end
#implementation UIAlertView(EnumView)
static BOOL _isInstanceMonitorStarted = NO;
+ (NSMutableArray *)instances
{
static NSMutableArray *array = nil;
if (array == nil)
array = [NSMutableArray array];
return array;
}
- (void)_newInit
{
[[UIAlertView instances] addObject:[NSValue valueWithNonretainedObject:self]];
[self _oldInit];
}
- (void)_oldInit
{
// dummy method for storing original init IMP.
}
- (void)_newDealloc
{
[[UIAlertView instances] removeObject:[NSValue valueWithNonretainedObject:self]];
[self _oldDealloc];
}
- (void)_oldDealloc
{
// dummy method for storing original dealloc IMP.
}
static void replaceMethod(Class c, SEL old, SEL new)
{
Method newMethod = class_getInstanceMethod(c, new);
class_replaceMethod(c, old, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
}
+ (void)startInstanceMonitor
{
if (!_isInstanceMonitorStarted) {
_isInstanceMonitorStarted = YES;
replaceMethod(UIAlertView.class, #selector(_oldInit), #selector(init));
replaceMethod(UIAlertView.class, #selector(init), #selector(_newInit));
replaceMethod(UIAlertView.class, #selector(_oldDealloc), NSSelectorFromString(#"dealloc"));
replaceMethod(UIAlertView.class, NSSelectorFromString(#"dealloc"), #selector(_newDealloc));
}
}
+ (void)stopInstanceMonitor
{
if (_isInstanceMonitorStarted) {
_isInstanceMonitorStarted = NO;
replaceMethod(UIAlertView.class, #selector(init), #selector(_oldInit));
replaceMethod(UIAlertView.class, NSSelectorFromString(#"dealloc"), #selector(_oldDealloc));
}
}
+ (void)dismissAll
{
for (NSValue *value in [UIAlertView instances]) {
UIAlertView *view = [value nonretainedObjectValue];
if ([view isVisible]) {
[view dismissWithClickedButtonIndex:view.cancelButtonIndex animated:NO];
}
}
}
#end
Start instance monitoring before using UIAlertView.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//...
//...
[UIAlertView startInstanceMonitor];
return YES;
}
Call dismissAll before showing another.
[UIAlertView dismissAll];
It's better using a singleton pattern if you can control all UIAlertViews.
But in my case, I need this code for closing javascript alert dialog in a UIWebView.

Since UIAlertView is deprecated in iOS8 in favor of UIAlertController (which is a UIViewController, presented modally), you can't preset 2 alerts at the same time (from the same viewController at least). The second alert will simply not be presented.
I wanted to partially emulate UIAlertView's behavior, as well as prevent showing multiple alerts at once. Bellow is my solution, which uses window's rootViewController for presenting alerts (usually, that is appDelegate's navigation controller). I declared this in AppDelegate, but you can put it where you desire.
If you encounter any sorts of problems using it, please report here in comments.
#interface UIViewController (UIAlertController)
// these are made class methods, just for shorter semantics. In reality, alertControllers
// will be presented by window's rootViewController (appdelegate.navigationController)
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSArray *)otherButtonTitles
handler:(void (^)(NSInteger buttonIndex))block;
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle;
#end
#implementation UIViewController (UIAlertController)
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
{
return [self presentAlertWithTitle:title message:message cancelButtonTitle:cancelButtonTitle
otherButtonTitles:nil handler:nil];
}
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
message:(NSString *)message
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSArray *)otherButtonTitles
handler:(void (^)(NSInteger buttonIndex))block
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
if (block)
block(0);
}];
[alert addAction:cancelAction];
[otherButtonTitles enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL *stop) {
UIAlertAction *action = [UIAlertAction actionWithTitle:title style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
if (block)
block(idx + 1); // 0 is cancel
}];
[alert addAction:action];
}];
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
UIViewController *rootViewController = appDelegate.window.rootViewController;
if (rootViewController.presentedViewController) {
[rootViewController dismissViewControllerAnimated:NO completion:^{
[rootViewController presentViewController:alert animated:YES completion:nil];
}];
} else {
[rootViewController presentViewController:alert animated:YES completion:nil];
}
return alert;
}
#end

Related

Category of UIAlertView need callback for alert button click iOS

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

How to perform reset of textfield using actionsheet?

I want to reset the textfields of myview to empty when an actionsheet destructive button is pressed. Done button calls the actionsheet.
This is my actionsheet:
- (IBAction)submit:(id)sender {
UIActionSheet *sheet=[[UIActionSheet alloc]initWithTitle:#"Options" delegate:sender cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Reset" otherButtonTitles:#"Save", nil];
[sheet showInView:self.view];
}
And is used this method to reset:
- (void)sheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex==0)
{
self.textf1.text = #"";
}
}
But Nothing is happening.
Replace delegate:sender to delegate:self that's why delegates
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
are not getting call also update the delegate function, once you done just set all textFiled to .text= #"". i hope it will work
Also posted as Comment earlier.
delegate method you are using is worng
Please make your delegate to self
delegate:self
and Use this method
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
////check index and empty all textfields
}
You should set self as the delegate, and I think the new UIAlertController is more convenience to use, without any delegate:
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:#"Action Sheet" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *reset = [UIAlertAction actionWithTitle:#"Reset" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
self.textf1.text = #"";
}];
actionSheet.actions = #[reset, cancel];
[self presentViewController:actionSheet animated:YES completion:nil]
Lots of issues:
Change this line:
if (buttonIndex == 0)
to:
if (buttonIndex == sheet.destructiveButtonIndex)
You also need to pass self as the delegate instead of sender.
UIActionSheet *sheet= [[UIActionSheet alloc] initWithTitle:#"Options" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Reset" otherButtonTitles:#"Save", nil];
And the name of the delegate method matters. You need:
- (void)actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)buttonIndex
See the docs for UIActionSheet. There are specific properties to get various button indexes. Use those over hardcoding index numbers.
Also note that UIAlertView is deprecated. You should be using UIAlertController unless you need to support iOS 7.
#import "ViewController.h"
#interface ViewController ()<UITextFieldDelegate,UIActionSheetDelegate>
{
UIActionSheet *sheet;
}
#property (weak, nonatomic) IBOutlet UITextField *txtText;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
//Button click
- (IBAction)OpenActionSheet:(id)sender
{
sheet=[[UIActionSheet alloc]initWithTitle:#"ActionSheetDemo" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Reset" otherButtonTitles:#"Save", nil];
[sheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex==0)
{
_txtText.text=#"";
}
}
#end

iOS 3D Touch in Objective C

Any one have any idea how can we implement 3d touch feature in objective C? I already check the app documentation but no example is related to objective C code.
Any sample code or example, how to implement ? Thanks in Advance.
The way I am doing this is to use a DFContinuousForceTouchGestureRecognizer that I have made available. This is a gesture recognizer extension that detects force touch and works alongside other gesture recognizers.
The DFContinuousForceTouchGestureRecognizer provides continuous updates about the pressure changes so you can do nice things like augment the view as the user varies their pressure on it, as opposed to just triggering a single event. If you just want a single event you can ignore eveything in the DFContinuousForceTouchDelegate except the - (void) forceTouchRecognized callback.
https://github.com/foggzilla/DFContinuousForceTouchGestureRecognizer
You can download this and run the sample app on a device that supports force press to see how it feels.
In your UIViewController implement:
- (void) viewDidLoad {
[super viewDidLoad];
_forceTouchRecognizer = [[DFContinuousForceTouchGestureRecognizer alloc] init];
_forceTouchRecognizer.forceTouchDelegate = self;
[self.imageView addGestureRecognizer:_forceTouchRecognizer];
}
implement the delegate protocol for force touch:
#pragma DFContinuousForceTouchDelegate
- (void) forceTouchRecognized:(DFContinuousForceTouchGestureRecognizer*)recognizer {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:#"Force Touch" message:#"YEAH!!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
});
}
- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer*)recognizer didStartWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
CGFloat transformDelta = 1.0f + ((force/maxForce) / 3.0f);
self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta);
[self.imageView setNeedsDisplay];
}
- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer*)recognizer didMoveWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
CGFloat transformDelta = 1.0f + ((force/maxForce) / 3.0f);
self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta);
[self.imageView setNeedsDisplay];
}
- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer*)recognizer didCancelWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer*)recognizer didEndWithForce:(CGFloat)force maxForce:(CGFloat)maxForce {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
- (void) forceTouchDidTimeout:(DFContinuousForceTouchGestureRecognizer*)recognizer {
self.imageView.transform = CGAffineTransformIdentity;
[self.imageView setNeedsDisplay];
}
Note that this will only be useful on a device that supports force touch.
Also you should not add the UIContinuousForceTouchGestureRecognizer to a view if are you running on iOS8 or under since it uses the new force property on UITouch only available in iOS9.
If you add this on iOS8 it will crash, so conditionally add this recognizer based on what iOS version you are running on if you are supporting versions older than iOS9.
Here is an example adapted from yeungkaho's example on github
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self handleTouches:touches];
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self handleTouches:touches];
}
-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self setForcePercentage:0];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self setForcePercentage:0];
}
-(void)handleTouches:(NSSet<UITouch *> *)touches{
for (UITouch *touch in touches) {
CGFloat force = touch.force;
CGFloat percentage = force/touch.maximumPossibleForce;
[self setForcePercentage:percentage];
break;
}
}
-(void)setForcePercentage:(CGFloat)percentage{
NSLog(#"LEVEL = %f",percentage*100);
}
I am sharing the source code for pressing on app icon populate 4 item in list
Step 1:- Import class in appDelegate.m
import sys/utsname.h
Step 2:-
#pragma MARK for Get Machine Name
- (NSString *) machineName {
struct utsname systemInfo;
uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if([[self machineName] isEqualToString:#"iPhone8,2"]|| [[self machineName] isEqualToString:#"iPhone8,1"]) {
[self addEventsFor3DTouchEvents];
}
return YES;
}
pragma MARK for Adding Action for Three D Touch Events
- (void) addEventsFor3DTouchEvents {
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) {
UIApplicationShortcutItem *item1 = [[UIApplicationShortcutItem alloc] initWithType:#"dynamic1" localizedTitle:TITLE_NAME_1 localizedSubtitle:#"" icon:[UIApplicationShortcutIcon iconWithTemplateImageName:TITLE_IMAGE_NAME_1] userInfo:nil];
UIApplicationShortcutItem *item2 = [[UIApplicationShortcutItem alloc] initWithType:#"dynamic1" localizedTitle:TITLE_NAME_2 localizedSubtitle:#"" icon:[UIApplicationShortcutIcon iconWithTemplateImageName:TITLE_IMAGE_NAME_2] userInfo:nil];
UIApplicationShortcutItem *item3 = [[UIApplicationShortcutItem alloc] initWithType:#"dynamic1" localizedTitle:TITLE_NAME_3 localizedSubtitle:#"" icon:[UIApplicationShortcutIcon iconWithTemplateImageName:TITLE_IMAGE_NAME_3] userInfo:nil];
UIApplicationShortcutItem *item4 = [[UIApplicationShortcutItem alloc] initWithType:#"dynamic1" localizedTitle:TITLE_NAME_4 localizedSubtitle:#"" icon:[UIApplicationShortcutIcon iconWithTemplateImageName:TITLE_IMAGE_NAME_4] userInfo:nil];
[[UIApplication sharedApplication] setShortcutItems: #[ item1, item2, item3, item4 ]];
}
}
#pragma mark - 3DTouch Delegate Methods
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
[self moveThrough3DTouch:shortcutItem];
}
#pragma MARK for Handling Action for Three D Touch Events
- (void)moveThrough3DTouch:(UIApplicationShortcutItem *)temp {
if ([temp.localizedTitle isEqualToString:TITLE_NAME_1]) {
[self.tabBarController setSelectedIndex:0];
} else if([temp.localizedTitle isEqualToString:TITLE_NAME_2]) {
[self.tabBarController setSelectedIndex:1];
} else if([temp.localizedTitle isEqualToString:TITLE_NAME_3]) {
[self.tabBarController setSelectedIndex:2];
} else if([temp.localizedTitle isEqualToString:TITLE_NAME_4]) {
[self.tabBarController setSelectedIndex:3];
}
}
**I am using Tab Bar controller in my app, If want to move in view controlled
- (void) moveToControllerScene {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORY_BOARD_IDENTIFIER bundle:nil];
YOUR_CONTROLLER_OBJECT *obj = [storyboard instantiateViewControllerWithIdentifier:#"YOUR_CONTROLLER_OBJECT"];
[navController pushViewController:obj animated:YES];
}

How to dismiss an UIAlertController and the keyboard simultaneously?

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()
}

UIPageViewController setViewController causing app to crash

I have a - (void)setViewControllers:(NSArray *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^)(BOOL finished))completion set up in a method. Whenever the method is called it doesn't go to the next page. Instead, it goes to the UIPageViewController storyboard and then crashes. I'm not sure what I'm doing wrong. I am using MSPageViewController for the pageviewcontroller, could that be it?
Heres my code:
UIViewController *viewcont = [[UIViewController alloc]init];
NSArray *viewControllers = [NSArray arrayWithObject:viewcont];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
Thanks.
There are 3 storyboards all conforming to MSPageViewControllerChild with the pageIndex property synthesized. IntroPageViewController is the first storyboard (p1).
PagingViewController.h:
//
// PagingViewController.m
// MordechaiLevi
//
// Created by Mordechai Levi on 4/10/14.
// Copyright (c) 2014 Mordechai Levi. All rights reserved.
//
#import "PagingViewController.h"
#import "IntroPageViewController.h"
#import "MSPageViewController.h"
#interface PagingViewController ()
#end
#implementation PagingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.device = [UIDevice currentDevice];
self.device.proximityMonitoringEnabled = YES;
if (self.device.proximityMonitoringEnabled == YES) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sensorCovered) name:#"UIDeviceProximityStateDidChangeNotification" object:nil];
}else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Uh Oh!" message:#"To use this app you need a device with a proximity sensor." delegate:self cancelButtonTitle:#"Got it" otherButtonTitles:nil, nil];
[alert show];
}
self.view.backgroundColor = [UIColor colorWithRed:0.2 green:0.859 blue:0.643 alpha:1.0];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
- (void)sensorCovered {
if (self.device.proximityState == YES) {
IntroPageViewController *viewcont = [[IntroPageViewController alloc]init];
NSArray *viewControllers = [NSArray arrayWithObject:viewcont];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
NSLog(#"sensor covered");
}else{
NSLog(#"sensor not covered");
}
}
- (NSArray *)pageIdentifiers {
return #[#"p1", #"p2", #"p3"];
}
#end
Looks like you're using MSPageViewController, with a controller that doesn't conform to MSPageViewControllerChild.
From the documentation:
Each of them [controllers] must be a class that conforms to MSPageViewControllerChild (if you don't need to add any extra functionality to it you can use MSPageViewControllerPage).

Resources