UIAlertView in another UIAlertView blocks keyboard input in the whole app - ios

The problems is that the iOS keyboard sometimes does not react anymore. So no input is possible.
If I uncomment the second UIAlertView in clickedButtonAtIndex it works fine.
The reason might be somewhere else? I have no idea ...
Thank you,
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex == 1)
{
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.05 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// User exists already
RequestObject * requestObject = [RequestObject sharedRequestObject];
[requestObject loginWithEmail:self.emailTextField.text andPassword:self.passwordTextField.text
success:^(UserVO *user) {
// ...
} failure:^(ErrorVO *error) {
// User does not exist, create a new one
if (error.code == ERROR_USER_UNKNOWN) {
// ...
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"ALERT_CAPTION_ERROR", "<Error>") message:[ErrorCode getErrorMessage:error] delegate:nil cancelButtonTitle:NSLocalizedString(#"ALERT_BUTTON_TEXT_OK", "<Ok>") otherButtonTitles:nil];
[alert show];
}
}];
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
}
}
The App uses storyboards
[self.window makeKeyAndVisible] is set

When MBProgressHUD starts then you are not able to do any interaction with UI. So it is not possible to do it without any built-in changes of MBProgressHUD.

Related

How to dismiss all the alertview from an array when select the cancel button in alertview?

How to dismiss all the alertview from an array when select the cancel button in alertview? I have 5 alertview in an array.. if i choose cancel from the first alertview then it will need to close remaining all alert instead of display.
for (NSDictionary *temp in [RMUserDefaults userDetails].SharedFolders)
{
NSString *name = temp[#"Name"];
sharedFolderId = [RMUserDefaults userDetails].SharedFolders[0][#"id"];
alert1= [[CustomUIAlertView alloc]initWithTitle:LString(#"RECEIPT_MATCH") message:[NSString stringWithFormat:#"%# has SharedFolders you to a Team Plan.", name] delegate:self cancelButtonTitle:LString(#"CANCEL") otherButtonTitles:[NSMutableArray arrayWithObjects:LString(#"Upgrade Now"),nil]];
alert1.tag = 12365;
[alert1 show];
double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
});
}
You can dismiss UIAlertView using below code.
UIWindow *window = [UIApplication sharedApplication].keyWindow;
for (UIView *view in w.subviews) {
if ([view isKindOfClass:[UIAlertView class]]) {
[(UIAlertView *)view dismissWithClickedButtonIndex:[(UIAlertView *)view cancelButtonIndex] animated:YES];
}
}
I hope, it will help you.
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"title" message:#"message" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[alert1 show];
[self performSelector:#selector(dismiss:) withObject:alert1 afterDelay:1.0];
add dismiss method
-(void)dismiss:(UIAlertView*)alert
{
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
UIWindow *Mywindow = [UIApplication sharedApplication].keyWindow;
for (UIView *costumview in w.subviews)
{
if ([view isKindOfClass:[UIAlertView class]])
{
[(UIAlertView *)view dismissWithClickedButtonIndex:[(UIAlertView *)view cancelButtonIndex] animated:YES];
}
}

how to do things between two UIAlertView (ios7)

I define a UIAlertView, its tag = 101, to determine save or not, show another UIAlertView called alertView2 when clicked save button, and then delete the subviews of rootView. But when I call clear Code [self clearAllSubviewsInRootView];here, it clear subViews before call alertView2. How do I fix it?
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 101)
{
if (buttonIndex == 0)
{
}
else
{
if (buttonIndex == 1)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"input fileName" message:nil delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:#"OK", nil];
alertView.tag = 102;
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show];
}
[self clearAllSubviewsInRootView];
}
}
if (alertView.tag == 102)
{
if (buttonIndex == 0)
{
}
else
{
NSArray *viewArray = [self.canvasView subviews];
NSUserDefaults *UD = [NSUserDefaults standardUserDefaults];
NSString *scaleStr = [UD objectForKey:#"scale"];
NSArray *dataArray = [NSArray arrayWithObjects:scaleStr, _labelArrivalTime.text, _textAccidentLocation.text,
_textDeclare.text, _textWeather.text, _textRoadSurface.text, [NSNumber numberWithFloat:canvasSize], nil];
NSMutableArray *array = [NSMutableArray arrayWithObjects:viewArray, dataArray, nil];
NSData * encodedata=[NSKeyedArchiver archivedDataWithRootObject:array];
NSString *fileName = [alertView textFieldAtIndex:0].text;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *floerName = #"file";
NSString *saveDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:floerName];
NSString *filePath = [saveDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.rta", fileName]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath])
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"file existed" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
alertView.tag = 103;
[alertView show];
}
else
{
[encodedata writeToFile:filePath atomically:YES];
[self saveImage:_prospector.image :filePath :#"勘查员"];
[self saveImage:_draftman.image :filePath :#"绘图员"];
[self saveImage:_person.image :filePath :#"当事人"];
}
}
}
}
UIAlertViews are modal views, but that does not mean they are synchronous.
In fact, UIAlertViews are asynchronous modal views.
In plain english, that means that they will be presented on screen, but some other code may be executed simultaneously (= asynchronous). So, code execution does not stop after calling [myAlert show]. The user however, is not able to select something else, he or she must deal with this one and only element on screen (= modal).
Having said that: I do not know the exact implementation of UIAlertViews, but it would not surprise me if the current runloop is being run to the end until the alert is actually presented on screen. That means, all code after [alertView show] will be executed to the end, and only then the alert will show (with the next runLoop).
So, you are asking "why it clears subviews before showing the second alert", but this is exactly what you tell it to do:
if (buttonIndex == 1)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"input fileName" message:nil delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:#"OK", nil];
alertView.tag = 102;
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show]; // <-- you show the alert
}
[self clearAllSubviewsInRootView]; // <—- and clear all views
You are creating and showing the second view and then immediately call [self clearAllSubviewsInRootView] after [alertView show].
If you want to clearAllSubviews only after the user has selected something in the second alert view, you must move this call [self clearAllSubviewsInRootView]to a later point in your if (alertView.tag == 102) routine
The main problem here js you override name of the alertView inside method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
You have here property named alertView. Later you declare:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"input fileName" message:nil delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:#"OK", nil];
This is a bit confusing and i'm not sure what are you trying to achieve here.
But GCD seems to fitting your problem so Apple provide here a useful snippet (you can call it by start writing dispatch_after):
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//put code here, whatever you want to fire later (after two seconds in this case)
});
In your case you probably want (i'm not sure) to keep second alert view after code is completed. In this case you should
This works for me,.. And it so simple you can use this for an infinite alertview.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if ([alertView.title isEqualToString:#"First Alertview Title"]) {
if (buttonIndex == [alertView cancelButtonIndex]) {
// do your stuff in cancel button --->First Alert
} else if (buttonIndex == 1) {
// do your stuff in other button ----> First Alert
}
} else if ([alertView.title isEqualToString:#"Second Alertview Title"]) {
if (buttonIndex == [alertView cancelButtonIndex]) {
// do your stuff in cancel button ----> Second Alert
} else if (buttonIndex == 1) {
// do your stuff in other button -----> Second Alert
}
}
}

Can an AlertView be timed out in an iOS app?

I want to know if it is possible for an AlertView to time out if it has been visible on the screen for a certain period of time without receiving any acknowledgement from the user, and if so, how? Is there a way for the AlertView object be linked with an NSTimer object?
My basic AlertView code is the following:
- (IBAction)showMessage:(id)sender {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Hello World!"
message:#"This is your first UIAlertview message."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
}
This is how I implemented in one of my apps
Inside the #interface declare your objects so you can keep track of them and add the if required
#property (nonatomic, strong) UIAlertView *myAlert;
#property (nonatomic, weak) NSTimer *myTimer;
In your code where you need to launch the alert add the following
self.myAlert = [[UIAlertView alloc]initWithTitle:#"TEST" message:#"TEST" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(cancelAlert) userInfo:nil repeats:NO];
[self.myAlert show];
Somewhere in your code add next function to dismiss the alert and invalidate the NSTimer
- (void)cancelAlert {
[self.myAlert dismissWithClickedButtonIndex:-1 animated:YES];
}
Also remember to invalidate the timer if a button is touched.
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {
[self.myTimer invalidate];
// Process pressed button
}
It may need some tweaks for your requirements.
Yes. Use dismissWithClickedButtonIndex:animated:
For example with a dispatch_after block, like this:
int64_t delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[message dismissWithClickedButtonIndex:message.cancelButtonIndex animated:YES];
});
If you want to use a NSTimer just save the UIAlertView in an instance variable so you can access it from within the timer method.
You could create a category for UIAlertView and add an observer which listens out and if it is triggered, removes itself:
#implementation UIAlertView (Cancellable)
+ (instancetype)cancellableAlertViewWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:delegate
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:nil];
if (otherButtonTitles != nil)
{
va_list args;
va_start(args, otherButtonTitles);
for (NSString *buttonTitle = otherButtonTitles; buttonTitle != nil; buttonTitle = va_arg(args, NSString*))
{
[alertView addButtonWithTitle:buttonTitle];
}
va_end(args);
}
[[NSNotificationCenter defaultCenter] addObserver:alertView selector:#selector(removeAlertView:) name:#"AlertsShouldBeCancelledNotification" object:nil];
return alertView;
}
- (void)removeAlertView:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self dismissWithClickedButtonIndex:-1 animated:YES];
}
#end
Then you could create an NSTimer in your main class and have it trigger the notification when the selector is called.
Use an NSTimer to call dismissWithClickedButtonIndex:animated: and invalidate it if the user clicks in time. Using dispatch_after risks sending the message to a released instance if the user has dismissed it already.
Take a look at this answer: dismissing a UIAlertView programmatically . Using performSelector:withObject:afterDelay: is much more elegant than building and tearing down a timer, in my opinion.

Cant dismiss UIAlertView, buttons not clickable

I have the following code where i show a MBProgress view and then run code in a separate thread. I then get a handle to the main thread and dismiss the spinner which works and then i show a UIAlertView. The UIAlertView loads fine however i can not click any of the buttons. If the alert view is outside of the dispatch block it works fine. Any ideas?
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
GamePlayManager *gameManager = [GamePlayManager alloc];
Session *sess = [Session sharedInstance];
//Add the last actor to the end of the list
NSMutableDictionary *connections = sess.connections;
[connections setObject:sess.secondActor forKey:[NSString stringWithFormat:#"%d",kLastFieldtag]];
BOOL result = [gameManager areAnswersCorrect:sess.connections startingActor:sess.firstActor endingActor:sess.secondActor];
NSString *display = #"Sorry incorrect. Please recheck your answers.";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Result"
message:display
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
if (result)
{
display = #"You are correct! You Won!";
if (sess.isMutiplayerGame)
{
[_gameCenterController endGame];
[self showGameOverScreen:YES isMultiplayer:YES];
}
else
{
[self showGameOverScreen:YES isMultiplayer:NO];
}
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[alert show];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[alert show];
});
}
});
This is likely an issue caused by a clash between the MBProgressHUD's animation and the UIAlertView's animation.
I've never used MBProgressHUD, but looking at the code on GitHub it seems they've already solved your problem. MBProgressHUD has a completionBlock property.
Code like this should work: (Warning: Untested)
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD HUDForView:self.view].completionBlock = ^{
[alert show];
};
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
MBProgressHUD fires its completionBlock after the view has finished its animation, so there should no longer be a conflict.
As a side note the MBProgressHUD method:
- (void)showAnimated:(BOOL)animated
whileExecutingBlock:(dispatch_block_t)block
onQueue:(dispatch_queue_t)queue
completionBlock:(MBProgressHUDCompletionBlock)completion;
seems like it would be a better fit for your code.
Declare the alert view outside the threads with block:
__block UIAlertView *alert;

How To Add Delay Before Presenting Second Alert View

Im displaying two Alert Views one after another in succession with the following:
-IBAction
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"my message" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert setDelegate:self];
[alert show];
}
- (void)didPresentAlertView:(UIAlertView *)alertView
{
[alertView setTitle:#"My new title"];
[alertView setMessage:#"My new message"];
}
The transition from the first Alert View to the second is so fast the user would not have time to read the fist message. Could someone advise how to add a delay between the Alerts. I Think I need to implement an NSTimer but implementing this is where I could use some advice.
I would suggest using dispatch_after, which can be inlined:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
Try this simple method:
- (void)alertView
{
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:#"Get Ready!" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[alertView show];
[self performSelector:#selector(dismissStartAlert:) withObject:alertView afterDelay:5];
}
-(void)dismissStartAlert:(UIAlertView *)alertView
{
[alertView dismissWithClickedButtonIndex:0 animated:YES];
[alertView setTitle:#"My new title"];
[alertView setMessage:#"My new message"];
[alert show];
}

Resources