Hook On to Exit Event of Application and Show an Alert - swift

I need to show an alert box when the user quits the application by clicking on the x button.How can I hook on to the exit event of the application and show an alert or view controller via a segue
performSegue(withIdentifier: "segue", sender: nil)
Please advice..

I know you want to use a segue to do this because they're just so convenient, but segues can't be done in a storyboard going from app delegate events like "applicationWillResignActive" (going to background) or "applicationWillBecomeActive" (becoming foreground again).
The correct way to do this would be via an alert. And you'd likely want to do this in applicationShouldTerminate, since you probably want to A) be able to abort quitting if you have a good reason not to quit just yet or B) give the user a choice whether or not to really quit.
Here's how it would look in swift 4:
var licenseWindowController : LicenseWindowController?
func dialogOKCancel(question: String, text: String) -> Bool {
let alert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
return alert.runModal() == .alertFirstButtonReturn
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let answer = dialogOKCancel(question: "Ok?", text: "Should we really quit?")
if answer == true
{
return .terminateNow
} else {
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let answer = dialogOKCancel(question: "Ok?", text: "Should we really quit?")
if answer == true
{
return .terminateNow
} else {
// to bring up a window from your storyboard...
let mainStoryboard = NSStoryboard.init(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
self.licenseWindowController = mainStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "LicenseWindowController")) as? LicenseWindowController
if let actualLicenseWC = self.licenseWindowController
{
actualLicenseWC.showWindow(self)
}
return .terminateCancel
}
}
Swift 3
var licenseWindowController : LicenseWindowController?
func dialogOKCancel(question: String, text: String) -> Bool {
let alert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
return alert.runModal() == NSAlertFirstButtonReturn
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplicationTerminateReply {
let answer = dialogOKCancel(question: "Ok?", text: "Should we really quit?")
if answer == true
{
return .terminateNow
} else {
let mainStoryboard = NSStoryboard.init(name: "Main", bundle: nil)
self.licenseWindowController = mainStoryboard.instantiateController(withIdentifier: "LicenseWindowController") as? LicenseWindowController
if let actualLicenseWC = self.licenseWindowController
{
actualLicenseWC.showWindow(self)
}
return .terminateCancel
}
}

In your appdelegate you should be able to put your code into applicationWillTerminate and make the message appear there.
Edit: You would probably be better off using a modal alert instead of a segue.

Related

Can't hide NSStatusItem from status bar?

I'm trying to add an option to my app settings to hide the icon from the status bar. It currently looks like this inside my AppDelegate :
func applicationDidFinishLaunching(_ notification: Notification) {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.button?.image = NSImage(systemSymbolName: "paperplane.fill", accessibilityDescription: nil)
if let menu = menu {
statusItem?.menu = menu
}
}
I'm trying to hide it from a preference pane view controller with the following code :
#IBAction func hideicon(_ sender: Any) {
if hideicon.state.rawValue == 1 {
NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength).isVisible = false
} else {
NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength).isVisible = true
}
}
But the button is still showing. Trying to edit the button image isn't working either.
What am I missing here?
This :
#IBAction func hideicon(_ sender: Any) {
if hideicon.state.rawValue == 1 {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
appDelegate.statusItem?.isVisible = false
} else {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
appDelegate.statusItem?.isVisible = true
}
}
Simply solved my issue.

How to make the page transition after clicking the button [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 1 year ago.
I'm developing an app to log book that children have read. So I would like to know how to make the page transition back to the menu page after clicking the "save" button. I also want to make the alert that shows "Data has been saved!". Below are my codes.
#IBOutlet weak var newBookSaveButton: UIButton!
#IBAction func newBookTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid,
let data = bookData() else {
return
}
db.collection("new reading").document(uid).setData(data)
}
func bookData() -> [String: Any]? {
guard let title = bookTitleTextField.text,
let author = bookAuthorTextField.text,
let summary = bookSummaryTextField.text else {
return nil
}
let data: [String: Any] = [
"bookTitle": title,
"bookAuthor": author,
"bookSummary": summary
]
return data
self.transitionToMenu()
}
func transitionToMenu() {
let MenuViewController = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboard.MenuViewController) as? MenuViewController
view.window?.rootViewController=MenuViewController
view.window?.makeKeyAndVisible()
}
}
With this code, I still unable to transition back to the Menu page. Your help are very much appreciated.
You can use this function:
func transitionToMenu() {
let alert = UIAlertController(title: nil, message: "Data has been saved!", preferredStyle: .alert)
alert.view.alpha = 0.5
alert.view.layer.cornerRadius = 15
self.present(alert, animated: true)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
alert.dismiss(animated: true)
if let navController = self.navigationController {
navController.popViewController(animated: true)
} else {
self.dismiss(animated: true, completion: {})
}
}
}

having trouble trying to keep users logged in with addStateDidChangeListener()

So my goal for now is to successfully keep users logged in and show a certain viewController depending if they're logged in or not. I've read a lot of the Stack questions that showed up first on Google searches about this same topic and they said use addStateDidChangeListener() and that's exactly what I did.
I didn't know how to approach this, so I watched a Youtube video and copied the exact code the guy had, his project did what I wanted mine to do, so I gave it a shot. Unfortunately when I run the simulator, sign in, exit the simulator and simulate again, nothing changes. I will add my code and it's location.
This is the code in my AppDelegate.swift in the didFinishLaunchingWithOptions method
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let auth = Auth.auth()
auth.addStateDidChangeListener { (_, user) in
switch user {
case nil:
guard self.activeViewController! is StudentSegmentedTableViewController else { return }
let nonLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
self.navigationController.setViewControllers([nonLoggedInViewController], animated: false)
self.navigationController.popToViewController(nonLoggedInViewController, animated: true)
self.activeViewController = nonLoggedInViewController
default:
guard self.activeViewController! is GothereMainMenuViewController else { return }
let alreadyLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
self.navigationController.setViewControllers([alreadyLoggedInViewController], animated: false)
self.navigationController.popToViewController(alreadyLoggedInViewController, animated: true)
self.activeViewController = alreadyLoggedInViewController
}
}
let nonLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
let alreadyLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
activeViewController = nonLoggedInViewController
switch Auth.auth().currentUser != nil {
case true:
activeViewController = alreadyLoggedInViewController
default:
break
}
navigationController = UINavigationController.init(rootViewController: activeViewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
I tried just this alone at first, and it didn't work so then I implemented a state listener in reasonable spots in my app.
First I added one that enables right after successful log in/signup and the segue is performed .
func enableAuth() {
authListener = Auth.auth().addStateDidChangeListener { (_, user) in
print("State Listener activated")
}
}
This is what I call in the viewDidLoad() of the segued viewController right after login/signup. To remove it, I simply call it when the logout button is pressed..
func disableAuthState() {
Auth.auth().removeStateDidChangeListener(self.authListener!)
print("State Listener Deactivated")
}
func studentLogoutSelected() {
var text = UITextField()
let alert = UIAlertController(title: "Logout", message: "Are you sure you want to logout?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
self.dismiss(animated: true, completion: nil)
}
let logoutAction = UIAlertAction(title: "Logout", style: .default) { (logoutAction) in
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
self.disableAuthState()
self.performSegue(withIdentifier: Constants.Segues.studentLogout, sender: self)
} catch let signOutError as NSError {
print("There was an error signing the user out. \(signOutError)")
}
}
alert.addAction(cancelAction)
alert.addAction(logoutAction)
present(alert, animated: true, completion: nil)
}
After all these functions and implementations, the shown blocks of code still don't do what I expected them to do. If anybody can point out issues or suggestions, that would be great, thanks.
First of all are you add FirebaseApp.configure() on your didFinishLaunchingWithOptions function in appdelegate? Then, Can you try call enableAuth in viewWillAppear()

How to ensure NSAlert pop up on window top?

The application is background only
This is a timer app
A dialog box(NSAlert) pops up after a specified time to prompt the user. How to ensure NSAlert on window top?
StatusBarMenu
class StatusBarMenu: NSObject {
private let statusItem: NSStatusItem!
init(statusItem: NSStatusItem) {
self.statusItem = statusItem
}
private func createMenu() {
selft.addMenuItem()
}
func refresh() {
self.statusItem.menu?.removeAllItems()
self.createMenu()
}
func addMenuItem() {
let item = NSMenuItem(title: "show alert", action: #selector(self.showAlert), keyEquivalent: "")
item.target = self
self.statusItem.menu?.addItem(item)
}
#objc func showAlert() {
DispatchQueue.main.sync {
let alert = NSAlert()
alert.icon = NSImage(named: "Alert")
alert.messageText = title
alert.informativeText = text
alert.alertStyle = .informational
alert.addButton(withTitle: "ok")
alert.runModal()
}
}
}
I found an answer to my own question:
alert.window.level = .floating
This works.
This is an example of how to keep NSAlert window on top
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
title, ok, cancel, other, message)
alert.setAlertStyle_(0) # informational style
# customize# floating window
alert.window().setLevel_(3)

How to check if the window is open or not?

I am opening a window say "Window2" from main menu item(Command 2 under Window) . So, if the menu item is clicked again it opens the "Window2" again instead of making the existing window the key window and disabling corresponding menu item. Below is my effort on implementing that in my AppDelegate class, as variable "lmWindowController" always returning nil, instances of window2 is getting opened multiple times, what am I missing here?
class AppDelegate: NSObject, NSApplicationDelegate {
var lmWindowController: LocalisationMappingWindowController?
func applicationDidFinishLaunching(_ notification: Notification) {
}
#IBAction func showLocalisationMappingWindowController(_ sender: Any) {
if (!(lmWindowController != nil)) {
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "LocalisationMapping"), bundle: Bundle.main)
if let windowController: NSWindowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "LocalisationMappingWindowController")) as? NSWindowController{
windowController.showWindow(windowController.window)
windowController.window?.makeKeyAndOrderFront(nil)
}
} else {
lmWindowController?.window?.makeKeyAndOrderFront(nil)
}
}
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
var enable = self.responds(to: menuItem.action)
if menuItem.action == #selector(showLocalisationMappingWindowController(_:)) {
for window in NSApplication.shared.windows {
if window.title == "Localization Mapping" {
enable = false
}
}
}
return enable
}
}
In showLocalisationMappingWindowController a new windowController is loaded but lmWindowController isn't changed. Add lmWindowController = windowController.
#IBAction func showLocalisationMappingWindowController(_ sender: Any) {
if (!(lmWindowController != nil)) {
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "LocalisationMapping"), bundle: Bundle.main)
if let windowController: NSWindowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "LocalisationMappingWindowController")) as? NSWindowController{
windowController.showWindow(windowController.window)
windowController.window?.makeKeyAndOrderFront(nil)
lmWindowController = windowController
}
} else {
lmWindowController?.window?.makeKeyAndOrderFront(nil)
}
}