Swift - how to change layout content in AppDelegate - ios

I try to show an alert when the app launches (a tabbed app)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let welcomeAlert = UIAlertController(title: "Turn on Bluetooth?", message: "Turn on Bluetooth?", preferredStyle: UIAlertControllerStyle.alert)
welcomeAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
action in self.alertServerClient()
}))
welcomeAlert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(welcomeAlert, animated: true, completion: nil)
return true
}
In alertServerClient() I want to change UIImage like
func alertServerClient() {
let vc = FirstViewController()
let imgBTOn = UIImage(named: "BT_Button.png")
vc.btnBT.setImage(imgBTOn, for: .normal)
}
Unfortunately, it doesn't work and throw error
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Is it possible to change UI content from AppDelegate?

Related

Presenting Alert Controller from the delegate

In context of this question/answer I've attempted but the result is no alert presented.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let alertController = UIAlertController(title: "Error", message: "ABc", preferredStyle: UIAlertController.Style.alert)
alertController.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
let win = UIWindow(frame: UIScreen.main.bounds)
let vc = UIViewController()
vc.view.backgroundColor = .clear
win.rootViewController = vc
win.windowLevel = UIWindow.Level.alert + 1 // Swift 3-4: UIWindowLevelAlert + 1
win.makeKeyAndVisible()
vc.present(alertController, animated: true, completion: nil)
return true
}
Attempt 2 based on the answer.
Use below line:
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
Instead of
vc.present(alertController, animated: true, completion: nil)
than problem is solved.
Example:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: {
let alertController = UIAlertController(title: "Alert Title", message:
"Message", preferredStyle: .actionSheet)
let okAction = UIAlertAction(title: "Ok", style:.default) {
UIAlertAction in
NSLog("OK Pressed")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
})

Swift display an alert after tapping on notification

So what i am trying to achieve is when a notification launch and the user tap on it, an Alert controller should appear with 2 options. However, when the app launch from the notification tap, nothing appears.
These codes are inside the AppDelegate.swift file
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
showAlert()
}
func showAlert() {
let alert = UIAlertController(title: "Confirm", message: "Confirm?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .destructive, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: nil))
window?.rootViewController?.present(alert, animated: true, completion: nil)
}
var topVC: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
topVC?.rootViewController = UIViewController()
let alert = UIAlertController(title: "Alert", message: "Notification Received", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in
// action OK
})
topVC?.makeKeyAndVisible()
topVC?.rootViewController?.present(alert, animated: true, completion: nil)
Try this:
func showAlert() {
var alertController = UIAlertController(title: "Confirm", message: "Confirm?", preferredStyle: UIAlertControllerStyle.alert)
var okAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default) {
UIAlertAction in
// action
}
var cancelAction = UIAlertAction(title: "No", style: UIAlertActionStyle.Cancel) {
UIAlertAction in
// action
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
}
I think you are calling the func showAlert() from the wrong place.
When the app launch from the notification tap, the app gets an event in
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
So you should try this,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//Handle remote notification event on app launch
if let remoteNotification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] {
showAlert()
}
}

Logout and reset UITabBarController

I'm aware that there are some questions out there that are similar to this question, however, a lot of them are in Objective C and I haven't been able to find a really applicable solution yet that works.
The main problem I am having is that when I log out of one account in my app, then log into another, the tab bar is not reset, and it displays the previously signed in users data. In other words, I need a way to "reset" the app back to the state it was in before any user had signed in.
I have tried to achieve this by writing a function inside App Delegate (setupTabBarController) and calling it when the user logs out, but no such luck yet.
This is what I have so far:
Logout code:
#objc func handleSignOutButtonTapped() {
let signOutAction = UIAlertAction(title: "Sign Out", style: .destructive) { (action) in
do {
try Auth.auth().signOut()
let welcomeControl = WelcomeController()
let welcomeNavCon = UINavigationController(rootViewController: welcomeControl)
self.present(welcomeNavCon, animated: true, completion: nil)
} catch let err {
print("Failed to sign out with error", err)
Service.showAlert(on: self, style: .alert, title: "Sign Out Error", message: err.localizedDescription)
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
Service.showAlert(on: self, style: .actionSheet, title: nil, message: nil, actions: [signOutAction, cancelAction]) {
}
let delegate = AppDelegate()
delegate.setupTabBarController()
}
Part of my App Delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
setupTabBarController()
return true
}
func setupTabBarController() {
window = UIWindow()
window?.makeKeyAndVisible()
let vc = MainTabBarController()
let controller = UINavigationController(rootViewController: vc)
window?.rootViewController = controller
}
Sign in code:
#objc func handleNormalLogin() {
hud.textLabel.text = "Signing In..."
hud.show(in: view, animated: true)
//TODO
guard let email = emailTextField.text else { return }
guard let password = passwordTextField.text else { return }
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if let error = error {
print("Error signing in: \(error)")
return
}
//sucessfully signed in
self.hud.dismiss(animated: true)
self.dismiss(animated: true, completion: nil)
}
}
Any help is really appreciated, I have been stuck on this for a few hours now and I really want to understand what I'm doing wrong.
Cheers
Problem
When you write let delegate = AppDelegate(). It means that you create a new AppDelegate. Instead of using current AppDelegate, you use another AppDelegate. That's why setupTabBarController method doesn't affects anything.
Calling setupTabBarController at the end of handleSignOutButtonTapped isn't a good idea. Because it will replace current rootViewController with UINavigation of MainTabBarController.
Answer
Use self.tabBarController? instead of self to present welcomeNavCon.
Don't call setupTabBarController at the end of handleSignOutButtonTapped method.
Recreate and set new viewControllers for MainTabBarController.
Code
#objc func handleSignOutButtonTapped() {
let signOutAction = UIAlertAction(title: "Sign Out", style: .destructive) { (action) in
do {
try Auth.auth().signOut()
let welcomeControl = WelcomeController()
let welcomeNavCon = UINavigationController(rootViewController: welcomeControl)
self.tabBarController?.present(welcomeNavCon, animated: true) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate;
appDelegate.resetTabBarController();
};
} catch let err {
print("Failed to sign out with error", err)
Service.showAlert(on: self, style: .alert, title: "Sign Out Error", message: err.localizedDescription)
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
Service.showAlert(on: self, style: .actionSheet, title: nil, message: nil, actions: [signOutAction, cancelAction]) {
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var tabBarController : UITabBarController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
setupTabBarController()
return true
}
func setupTabBarController() {
window = UIWindow()
window?.makeKeyAndVisible()
let vc = MainTabBarController()
let controller = UINavigationController(rootViewController: vc)
window?.rootViewController = controller
}
func resetTabBarController() -> Void {
let viewControllerAtIndex1 = ...
let viewControllerAtIndex2 = ...
let viewControllerAtIndex3 = ...
tabBarController?.viewControllers = [viewControllerAtIndex1, viewControllerAtIndex2, viewControllerAtIndex3];
}
}

How to add method from VC in alert handler/closure in DidReceiveLocalNotification?

everyone!
When my app in foreground I want it to show alert when DidReceiveLocalNotification triggered.
I can add alert to mimic local notifications in AppDelegate.swift, but the problem is I don't know how to add method from my ViewController to UIAlertAction closure (see commented line), to finish animation when timer stopped.
My code below:
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
let alertTimerEnds = UIAlertController(title: "Timer finished!", message: nil, preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default) { finished in
print("You've pressed OK button")
//self.ViewController().finishAnimation()
}
alertTimerEnds.addAction(okAction)
self.window?.rootViewController?.presentViewController(alertTimerEnds, animated: true, completion: nil)
}
Maybe I should do it in ViewController usind AppDelegate?
let someAppDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
someAppDelegate?.application(UIApplication.sharedApplication(), didReceiveLocalNotification: UILocalNotification) { code for alert }
If you want to do that
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification)
{
var viewController : UIViewController = (application.keyWindow?.rootViewController)!
while ((viewController.presentedViewController) != nil) {
viewController = viewController.presentedViewController!
}
let alert = UIAlertController(title: "", message: notification.alertBody, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: {(action: UIAlertAction!) in}))
viewController.presentViewController(alert, animated: true, completion: nil)
})
Show UIAlertController in didReceiveLocalNotification method
Try with below code, this will display alert on viewcontroller which is present at a moment.
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
var latestViewController : UIViewController!
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
if let viewControllers = appDelegate.window?.rootViewController?.presentedViewController {
latestViewController = viewControllers as UIViewController
}
else if let viewControllers = appDelegate.window?.rootViewController?.childViewControllers {
latestViewController = viewControllers.last! as UIViewController
}
//var alert: UIAlertView!
//alert = UIAlertView(title: "Title", message:"Message , delegate: nil, cancelButtonTitle:"Ok" )
//alert.show()
let alert = UIAlertController(title: "Title", message:"Message", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
// Put here any code that you would like to execute when
// the user taps that OK button (may be empty in your case if that's just
// an informative alert)
}
alert.addAction(action)
latestViewController.presentViewController(alert, animated: true){}
}
How do I migrate from UIAlertView (deprecated in iOS8)

UIAlert Controller not working in AppDelegte didFinishLaunchingWithOptions method - in Swift

I am trying to make a UIAlertController pop up one time when the user first downloads the app. However, I get the following error when I put the code in didFinishLaunchingWithOptions,
2015-01-15 23:54:45.306 WeddingApp Warning: Attempt to present UIAlertController: on UITabBarController: whose view is not in the window hierarchy!
My code is below:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
if window == nil {
println("window is nil");
return true;
}
if window!.rootViewController == nil {
println("window!.rootViewController is nil");
return true;
}
//TabBarController//
let tabBarController: UITabBarController = window!.rootViewController! as UITabBarController;
UITabBar.appearance().tintColor = UIColor.magentaColor();
UITabBar.appearance().translucent = false;
/* tableView cells are now completely visable. They do not hie behind the tabBar */
tabBarController.viewControllers = [
TwelveToTenMonths(nibName: nil, bundle:nil),
NineToSevenMonths(nibName: nil, bundle:nil),
SixToFourMonths(nibName: nil, bundle:nil),
ThreeToOneMonth(nibName: nil, bundle:nil),
];
var alert = UIAlertController(
title: "Welcome to WeddingApp!",
message: "Thank You for choosing the \nWedding App for your organization needs. \n\nFor more great tools please visit our website www.wedme.com",
preferredStyle: UIAlertControllerStyle.Alert);
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.Default, handler: nil));
self.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil);
return true
}
***NOTE: I also tried adding the following to the appDelegate instead of the above code but the alert continues to appear whenever I return to the app.....
func applicationDidBecomeActive(application: UIApplication) { //Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
showAlert();
}
func showAlert(){
println("alert");
var alert = UIAlertController(
title: "Welcome to WeddingApp!",
message: "Thank You for choosing the \nWedding App for your organization needs. \n\nFor more great tools please visit our website www.wedme.com",
preferredStyle: UIAlertControllerStyle.Alert);
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.Default, handler: nil));
self.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil);
}
Does anyone know a way around this???
In the first case, you never added the UIAlertController (you could try adding it as a child view controller).
https://stackoverflow.com/a/17012439/3324388
In the second case you assigned the root controller to the UIAlertController and never assign the root controller back to the UITabBarController. You need to set it back.
Have you tried using a UIAlertView instead?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html
Try something like this:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
if self.window!.rootViewController as? UITabBarController != nil {
var tababarController = self.window!.rootViewController as UITabBarController
.
.
.
let alertController = UIAlertController(title: "Welcome...", message: "Thank You...", preferredStyle: .Alert)
tababarController.presentViewController(alert, animated: true, completion: nil);
}
return true
}

Resources