I have the following function that pops up a UIAlert which allows the user to update their Haptic Feedback setting:
- (void)requestHapticSetting{
UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
alertWindow.rootViewController = [[UIViewController alloc] init];
alertWindow.windowLevel = UIWindowLevelAlert + 1;
[alertWindow makeKeyAndVisible];
if(isHapticOn){
hapticMessage = #"Haptic feedback is currently\nturned ON.\nPlease update preference.";
}
else {
hapticMessage = #"Haptic feedback is currently\nturned OFF.\nPlease update preference.";
}
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"Haptic Setting"
message:hapticMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* onAction = [UIAlertAction actionWithTitle:#"TURN ON" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self saveHapticSettingOn];
}];
UIAlertAction* offAction = [UIAlertAction actionWithTitle:#"TURN OFF" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self saveHapticSettingOff];
}];
[alert addAction:offAction];
[alert addAction:onAction];
[alertWindow.rootViewController presentViewController:alert animated:YES completion:nil];
}
I have used this for a couple of years and it works great.
However, since updating to iOS 13 and updating to the latest Xcode, my alert pops up for less than a second before closing.
What has changed that could be making this happen? Thanks in advance.
What seems to have changed is that on iOS 12 and previous versions your app would hold a strong reference to the window just by calling [alertWindow makeKeyAndVisible];, in iOS 13 it doesn't anymore.
What's happening is that the only strong reference to your alertWindow is in your requestHapticSetting func, and as soon as this func returns, the window is destroyed, thus removing your alert from the view.
This might be fixed just by adopting iOS 13 scenes, but I haven't tested that. What I can suggest, which won't properly work if you are using scenes, is holding your alert window in a strong variable somewhere in your code, and then using it to present the alert. I'd suggest doing so in a singleton or AppDelegate itself.
//AppDelegate.h
...
#property (strong) UIWindow *alertWindow;
....
//AppDelegate.m
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
self.alertWindow = [[UIWindow alloc] init];
self.alertWindow.rootViewController = [[UIViewController alloc] init];
self.alertWindow.windowLevel = UIWindowLevelAlert + 1;
...
}
...
//Your class that's presenting the alert
#import "AppDelegate.h"
...
- (void)requestHapticSetting{
AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication;
[appDelegate.alertWindow makeKeyAndVisible];
if(isHapticOn){
hapticMessage = #"Haptic feedback is currently\nturned ON.\nPlease update preference.";
} else {
hapticMessage = #"Haptic feedback is currently\nturned OFF.\nPlease update preference.";
}
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"Haptic Setting"
message:hapticMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* onAction = [UIAlertAction actionWithTitle:#"TURN ON" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self saveHapticSettingOn];
[appDelegate.alertWindow setHidden:YES];
}];
UIAlertAction* offAction = [UIAlertAction actionWithTitle:#"TURN OFF" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self saveHapticSettingOff];
[appDelegate.alertWindow setHidden:YES];
}];
[alert addAction:offAction];
[alert addAction:onAction];
[appDelegate.alertWindow.rootViewController presentViewController:alert animated:YES completion:nil];
}
For Swift code, check this answer.
I have created a helper class which support new UIWindowScene and iOS 13.X and swift 5.X. You guys can try
https://github.com/emraz/ERAlertController
Related
I have 2 alerts I want to present in different cases, I wrote a general function to init the alerts in the beginning and change the messages later, but when I am trying to present the alert I get a crash. When I inspect the notesAlert in runtime it is still nil.
Can someone explain what I did wrong?
#interface viewController (){
UIAlertController *tableAlert;
UIAlertController *notesAlert;
}
#end
#implementation viewController
- (void)viewDidLoad {
[super viewDidLoad];
[self initAlert:tableAlert];
[self initAlert:notesAlert];
}
// func to init the alerts
-(void)initAlert:(UIAlertController*)alert{
alert = [UIAlertController alertControllerWithTitle: #"" message: #"" preferredStyle:UIAlertControllerStyleActionSheet];
[alert setModalPresentationStyle:UIModalPresentationPopover];
[alert.popoverPresentationController setSourceView:self.view];
UIPopoverPresentationController *popover = [alert popoverPresentationController];
CGRect popoverFrame = CGRectMake(0,0, self.view.frame.size.width/2, self.view.frame.size.width/2);
popover.sourceRect = popoverFrame;
UIAlertAction *dismiss = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:dismiss];
}
- (IBAction)showNotes:(id)sender {
// here the notesAlert is still nil
[notesAlert setTitle:#"oops"];
[notesAlert setMessage:#"you pressed the wrong one"];
[self presentViewController:notesAlert animated:YES completion:nil];
}
#end
[self initAlert: notesAlert]; doesn't create notesAlert. Instead, you could use notesAlert = [self initAlert];
Maybe something like this:
#interface ViewController () {
UIAlertController *tableAlert;
UIAlertController *notesAlert;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableAlert = [self initAlert];
self.notesAlert = [self initAlert];
}
// func to init the alerts
- (UIAlertController *) initAlert {
UIAlertController *alert = [UIAlertController alertControllerWithTitle: #"" message: #"" preferredStyle: UIAlertControllerStyleActionSheet];
[alert setModalPresentationStyle:UIModalPresentationPopover];
[alert.popoverPresentationController setSourceView: self.view];
UIPopoverPresentationController *popover = [alert popoverPresentationController];
CGRect popoverFrame = CGRectMake(0,0, self.view.frame.size.width/2, self.view.frame.size.width/2);
popover.sourceRect = popoverFrame;
UIAlertAction *dismiss = [UIAlertAction actionWithTitle: #"Ok" style: UIAlertActionStyleDefault handler:nil];
[alert addAction: dismiss];
return alert;
}
There is a game that i load in the WKWebView and this Alert and textField appear after the game is finished. I want to display keyboard when user taps on the textField. But the problem is there is no code to display this alert and textfield, otherwise i would have managed the textField to becomeFirstResponder().
Desperately need some help. I would really appreciate it.
Okay so i have found the solution. I had a textField in custom Alert/Popup in WkwebView and i wanted the textField to becomeFirstResponder() so i could take user input.
// So i Implemented WKUIDelegate..
#import <WebKit/WebKit.h>
#interface MyController : UIViewController<WKUIDelegate>
Assigned the Delegate..
- (void)viewDidLoad {
[super viewDidLoad];
_webkitView.UIDelegate = self;
}
//implemented delegate function.
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *result))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"BlockChaid Id, Email or Username"
message:prompt
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = prompt;
textField.secureTextEntry = NO;
textField.text = defaultText;
}];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(#"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
completionHandler(nil);
}]];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(#"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
completionHandler([alert.textFields.firstObject text]);
}]];
[self presentViewController:alert animated:YES completion:nil];
}
After successful build, this was the output.
I tapped the textField, and javaScript Alert appeared.
This right here is what i wanted to achieve.
Maybe i was not good enough to explain my question, but this is what i was asking for. Thank you :)
I have a UIAlertController in my AppDelegate and present it with my rootViewController when I receive a notification. But when the alert view pops up, it quickly dismisses itself instead of waiting for a tap event on the "Ok" button... Here's my code in AppDelegate.m. Any idea? Thanks!
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[self showNotification:"Success message here"];
}
- (void)showNotification:(NSString *)text {
UIAlertController* avc = [UIAlertController alertControllerWithTitle:#"Success" message:text
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleCancel handler:nil];
[avc addAction:ok];
[self.window.rootViewController presentViewController:avc animated:true completion:nil];
}
EDIT: I have another view controller; call it X. X's viewDidLoad function has a timer counting down before X dismisses itself and presents another view controller. I noticed an interesting timing issue here: when the notification comes slowly enough so that the UIAlertController appears after X's timer is up and another view controller has been presented, the alertController doesn't dismiss itself. If the opposite happens - the notification comes quickly enough making the UIAlertController appears before X dismisses itself, then the whole AlertController will be flushed when the timer is up (which makes sense, I guess...). How can I prevent this from happening?
You can use perform selector method, like this:
- (void)showNotification:(NSString *)text {
UIAlertController* avc = [UIAlertController alertControllerWithTitle:#"Success" message:text
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleCancel handler:nil];
[avc addAction:ok];
[self performSelector:#selector(dissmissAlert:) withObject:avc afterDelay:3.0];
[self.window.rootViewController presentViewController:avc animated:true completion:nil];
}
-(void)dissmissAlert:(UIAlertController *) alert{
[alert dismissViewControllerAnimated:true completion:nil];
}
In my project i am using xib's in that i want to show UIAlertController and perform some actions.but i am getting error.
Here is my code.
What is the mistake in my code.
when add xibView in UIVieController assign self in ParentViewController.
i.e
_xibView *objxibView = [[xibView alloc]init];
objxibView.parentViewController = self;
in Xib's xibView.h file create 1 property
#property (weak, nonatomic) UIViewController *parentViewController;
and in xibView.m File use below code
for UIAlertController in xib
{
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *actionOK = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:actionOK];
[self.parentViewController presentViewController:alert
animated:YES
completion:nil];
}
UIAlertView and UIActionSheet not displaying properly in iOS 8, its hiding the view controller, also In alert view title is not displaying. I am running the app from Xcode 5.1.1.
Did you added any category for ViewController in your application. like below shown
#implementation UIViewController (CustomeAction)
- (void)setTitle:(NSString *)title{
// YOU CODE///
}
#end
This may the issue. I have solved by removing this category - (void)setTitle:(NSString *)title.
Note: From iOS 8, UIAlertViewController is inherited from UIViewController. If you use category method, it will be effect on UIAlertView & UIActionSheet titles.
You can refer apple docs at UIAlertController Class Reference
It should be noted, that UIAlertView and UIActionSheet is deprecated in iOS 8. You can check out this question or the apple link. May be it will help you.
You can use following code using UIAlerController for XCode 6 and IOS 8
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:#"Title"
message:#"Your Message"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:#"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
For ActionSheet
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:#"Title"
message:#"Your Message"
preferredStyle:UIAlertControllerStyleActionSheet];