How to prevent NSAlert from dismiss - swift

How can I prevent a simple NSAlert from being dismissed?
F.ex., when the application launches, I show a NSAlert with a NSTextField included.
The user shall type in a password. Only if the password is correct, the user should be allowed to use the application, if not (if the password is not correct), the Alert should stay there and ask again for the password.
This is my code so far (to create the alert):
func applicationDidFinishLaunching(_ aNotification: Notification){
let alert = NSAlert()
alert.addButton(withTitle: "Send")
alert.delegate = self
alert.alertStyle = .informational
alert.messageText = "Password - Login"
alert.informativeText = "Please type in your password: "
let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
txt.stringValue = "Password:"
alert.accessoryView = txt
alert.beginSheetModal(for: NSApplication.shared().mainWindow!) { (response) in
if (response == NSAlertFirstButtonReturn) {
// the alert closes here, is there any way to prevent this?
} else {
print("No value.")
}
}
OS: OS X Sierra,
Swift 3

You can present the alert a second time; if you need to customize the behavior beyond that, you'll need to eschew NSAlert and run an NSWindow or NSPanel of your own making.

Related

StatusBar menu does not show up when using external screen

I'm using the code below to show the status bar menu on the right click and show the application on the left click.
To achieve that, we need to detach the menu (statusBarItem.menu = nil) once it is closed. Otherwise, we would stop receiving statusBarButtonDidClick event and left-click would not work.
However, when I attach an external screen after right-click menu is getting closed immediately. The problem is in the line: statusBarItem.button?.performClick(nil), but without that menu would not appear instantly.
My guess is that performClick is trying to click on another screen which closes the menu on the active screen.
Is there any way to fix it or achieve the same functionality without performClick?
func createStatusBarItem() {
statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.squareLength))
if let button = statusBarItem.button {
button.image = Asset.Icons.statusbar.image
button.image?.size = CGSize(width: 16, height: 16)
button.target = self
button.action = #selector(statusBarButtonDidClick)
}
statusBarItem.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
func menuDidClose(_ menu: NSMenu) {
statusBarItem.menu = nil
}
#objc private func statusBarButtonDidClick(sender: NSStatusBarButton) {
let event = NSApp.currentEvent!
if event.type == NSEvent.EventType.rightMouseUp {
let statusBarMenu = StatusBarMenu()
statusBarMenu.delegate = self
statusBarItem.menu = statusBarMenu
statusBarItem.button?.performClick(nil)
} else {
application.showApplication()
}
}

WKWebView removes automatically filled in data

I try to automatically fill in a login formula and confirm data by clicking the login button. I would like to use Swift 5, macOS 10.15 and a WKWebView.
The first step is done, I can fill in my login data like this:
import Cocoa
import WebKit
class ViewController: NSViewController, WKUIDelegate, WKNavigationDelegate {
let webView = WKWebView()
let button = NSButton()
override func viewDidLoad() {
super.viewDidLoad()
button.frame = CGRect(x: 400, y: 0, width: 40, height: 20)
button.image = NSImage(named: NSImage.Name("Button"))
button.target = self
button.action = #selector(self.fillIn)
self.view.addSubview(button)
self.webView.uiDelegate = self
self.webView.navigationDelegate = self
webView.frame = CGRect(x: 0, y: 0, width: 400, height: 270)
view.addSubview(webView)
let url = URL(string: "https://lobby.ogame.gameforge.com/de_DE/hub")
let request = URLRequest(url: url!)
webView.load(request)
}
#objc func fillIn() {
setWebViewValue(name: "email", data: "user#web.de")
setWebViewValue(name: "password", data: "pwdpwd")
}
func setWebViewValue(name: String, data: String) {
let jsFunc = "document.getElementsByName(\"\(name)\")[0].value = \"\(data)\""
webView.evaluateJavaScript(jsFunc) {(result, error) in
print(result, error)
}
}
}
But when I click the login button, all data will be removed. So the email and password input fields are empty while logging in and I get an invalid input data error.
Example
Please note, even if the email address is wrong, the address and password input fields are not cleared when clicking to the login button.
Question:
Why is the auto-filled data deleted when you click the login button and why is the data that was entered normally not deleted?
Check the javascript code on the page to see if it is clearing the fields on it's own or maybe it was waiting for typing into the fields.

how to show loading at the login request in swift

When the login process is of type Post
using Alamofire
I want to show the sign in progress while waiting for the server
....
For example, such as the view above the login screen appears when you press the login button and disappear when you enter the home screen
This solution did not work :::
func show () {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
vc.present(alert, animated: true, completion: nil)
// vc.dismiss(animated: false, completion: nil)
}
If you want to use CocoaPods, use https://cocoapods.org/pods/SVProgressHUD
To use it, call SVProgressHUD.show()
right before sending the http request, and then call SVProgressHUD.dismiss() once the login is successful.
Hope it helps.
Cheers.

Show please wait overlay in swift asynchronously

I am using the answer located here for creating a "please wait" overlay. And I also used this answer for the asynchronous logic. What I am trying to do is create this overlay asynchronously. Ultimately, what I would like to do is just create a method which shows this overlay, and when that method call returns I want to be sure that the overlay has been fully presented. So something like this:
Helper.showPleaseWaitOverlay()
doSomeOtherTask() // when we get here, overlay should be fully presented
Helper.hidePleaseWaitOverlay()
I realize I could do something like this (e.g. use the completion callback of the present method) :
Helper.showPleaseWaitOverlay() {
doSomeOtherTask()
Helper.hidePleaseWaitOverlay()
}
But I really am just curious as to why the below code doesn't work. What ends up happening is that the group.wait() call just hangs and never returns.
What am I doing wrong?
// create a dispatch group which we'll use to keep track of when the async
// work is finished
let group = DispatchGroup()
group.enter()
// create the controller used to show the "please wait" overlay
var pleaseWaitController = UIAlertController(title: nil, message: "Please Wait...", preferredStyle: .alert)
// present the "please wait" overlay as an async task
DispatchQueue.global(qos: .default).async {
// we must perform the GUI work on main queue
DispatchQueue.main.async {
// create the "please wait" overlay to display
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
self.pleaseWaitController!.view.addSubview(loadingIndicator)
self.present(self.pleaseWaitController!, animated: true) {
// the "please wait" overlay has now been presented, so leave the dispatch group
group.leave()
}
}
}
// wait for "please wait" overlay to be presented
print("waiting for please wait overlay to be presented")
group.wait() // <---- Call just hangs and never completes
print("done waiting for please wait overlay to be presented")

Swift: Add Directions button to pin annotation

I have an app that displays the company location using an MKMapView. When the user visits the contact page a map appears at the top of the screen where a pin drops and it shows our location. When the map pin is clicked it shows the company name in the annotation.
I am pulling the company address into the MKMApView from data stored in my Parse backend using back4app.
I have this all working perfectly, however, when I click the map pin and it shows the company name, I am trying to get a car icon button in there also so the user can get directions to our company.
Here is the code I have to set the annotation:
func addPinOnMap(_ address: String) {
var hotelClass = PFObject(className: HOTEL_CLASS_NAME)
hotelClass = hotelArray[0]
if mapView.annotations.count != 0 {
annotation = mapView.annotations[0]
mapView.removeAnnotation(annotation)
}
// Make a search on the Map
localSearchRequest = MKLocalSearchRequest()
localSearchRequest.naturalLanguageQuery = address
localSearch = MKLocalSearch(request: localSearchRequest)
localSearch.start { (localSearchResponse, error) -> Void in
// Add PointAnnonation text and a Pin to the Map
self.pointAnnotation = MKPointAnnotation()
self.pointAnnotation.title = "\(hotelClass[HOTEL_NAME]!)"
self.pointAnnotation.coordinate = CLLocationCoordinate2D( latitude: localSearchResponse!.boundingRegion.center.latitude, longitude:localSearchResponse!.boundingRegion.center.longitude)
self.pinView = MKPinAnnotationView(annotation: self.pointAnnotation, reuseIdentifier: nil)
self.mapView.centerCoordinate = self.pointAnnotation.coordinate
self.mapView.addAnnotation(self.pinView.annotation!)
// Zoom the Map to the location
self.region = MKCoordinateRegionMakeWithDistance(self.pointAnnotation.coordinate, 1000, 1000);
self.mapView.setRegion(self.region, animated: true)
self.mapView.regionThatFits(self.region)
self.mapView.reloadInputViews()
}
}
I simply do not know how I can get the button icon in the annotation and show directions upon click of the button. either using the current map view or to open the users maps app on their device, either way is fine.
Thank you.
I propose you a sample method to had a button on your pin and to give to the user the direction of the pin with the apple map app.
First, after your pin's declaration, you should declare a button witch give you the opportunity to interact with the user in the pin (we will show later how to initialize the button in the pin).
You can declare your button like this:
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPointZero, size: smallSquare))
Note, smallSquare give to the button the correct size of your future pin's button.
You can add a image in the pin's button with the method: button.setBackgroundImage(UIImage(named: "car"), forState: .Normal).
Apple developer give to us some documentation: Apple developer web site.
Then you can had a button action with this method: button.addTarget(self, action: #selector(ViewController.getDirections), forControlEvents: .TouchUpInside).
So, like this, if the button is selected normally (.TouchUpInside), the program calls the function getDirection ().
Now, you can initialize the button in your pin with the method: pinView?.leftCalloutAccessoryView = button.
Note this method set the button in the left of the pin. You can put the button in the right like this: pinView?.rightCalloutAccessoryView = button.
Apple developer give some informations on the subject.
However, I do not know how to initialize the button in the totally surface of the pin.
Finally, you should have the function getDirections ()to give to the user the "direction" of the pin.
I propose you to use the apple map app for this purpose.
My function getDirections () look like this:
func getDirections(){
guard let selectedPin = selectedPin else { return }
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMapsWithLaunchOptions(launchOptions)
print ("GET DIRECTION")
}
I find this function in this web site. I think it can be explain this better than me.
At the end my program looks like this:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView?{
guard !(annotation is MKUserLocation) else { return nil }
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
pinView?.pinTintColor = UIColor.orangeColor()
pinView?.canShowCallout = true
//The initialization of the pin. Here your program looks great.
// And after the code as seen above.
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPointZero, size: smallSquare))
button.setBackgroundImage(UIImage(named: "car"), forState: .Normal)
button.addTarget(self, action: #selector(ViewController.getDirections), forControlEvents: .TouchUpInside)
pinView?.leftCalloutAccessoryView = button
return pinView
}
func getDirections(){
guard let selectedPin = selectedPin else { return }
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMapsWithLaunchOptions(launchOptions)
print ("GET DIRECTION")
}
Normally, your program should look like this:
func addPinOnMap(_ address: String) {
var hotelClass = PFObject(className: HOTEL_CLASS_NAME)
hotelClass = hotelArray[0]
if mapView.annotations.count != 0 {
annotation = mapView.annotations[0]
mapView.removeAnnotation(annotation)
}
// Make a search on the Map
localSearchRequest = MKLocalSearchRequest()
localSearchRequest.naturalLanguageQuery = address
localSearch = MKLocalSearch(request: localSearchRequest)
localSearch.start { (localSearchResponse, error) -> Void in
// Add PointAnnonation text and a Pin to the Map
self.pointAnnotation = MKPointAnnotation()
self.pointAnnotation.title = "\(hotelClass[HOTEL_NAME]!)"
self.pointAnnotation.coordinate = CLLocationCoordinate2D( latitude: localSearchResponse!.boundingRegion.center.latitude, longitude:localSearchResponse!.boundingRegion.center.longitude)
self.pinView = MKPinAnnotationView(annotation: self.pointAnnotation, reuseIdentifier: nil)
self.mapView.centerCoordinate = self.pointAnnotation.coordinate
self.mapView.addAnnotation(self.pinView.annotation!)
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPointZero, size: smallSquare))
button.setBackgroundImage(UIImage(named: "car"), forState: .Normal)
button.addTarget(self, action: #selector(ViewController.getDirections), forControlEvents: .TouchUpInside)
self.pinView?.leftCalloutAccessoryView = button
// Zoom the Map to the location
self.region = MKCoordinateRegionMakeWithDistance(self.pointAnnotation.coordinate, 1000, 1000);
self.mapView.setRegion(self.region, animated: true)
self.mapView.regionThatFits(self.region)
self.mapView.reloadInputViews()
}
func getDirections(){
guard let selectedPin = selectedPin else { return }
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMapsWithLaunchOptions(launchOptions)
print ("GET DIRECTION")
}
Excuse me, I can not try your code because I have not your totally code like the pinView declaration.
Note, you can need to add this line of code to accept than the pin can show callout pinView?.canShowCallout = true.
You can consult this webs sites if you want more details or tell me a comment.
Thorn technologies
Apple developer