OSX Swift 3 Alert als Sheet mit Rückgabewert

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • OSX Swift 3 Alert als Sheet mit Rückgabewert

    Hallo,

    folgende Situation stellt sich mir da. Ich möchte ein Alert als Sheet (also gebunden an das Applicationwindow, damit der Anwender dies direkt in der App vor Augen hat) mit Rückgabewert erzeugen. Nur wenn ich den Altert mit alert.beginSheetModal(....) starten möchte dann erhalte ich keinen Rückgabewert bzw. den Hinweis, dass diese Methode als Void deklariert ist. Kann mir jemand helfen, ausgehend von folgendem Quellcode:

    Quellcode

    1. func dialogHinweisOKCancel(question: String, text: String) -> Bool {
    2. let alert = NSAlert()
    3. alert.messageText = question
    4. alert.informativeText = text
    5. alert.alertStyle = NSAlert.Style.critical
    6. alert.addButton(withTitle: "YES")
    7. alert.addButton(withTitle: "No")
    8. return alert.runModal() == NSApplication.ModalResponse.alertFirstButtonReturn
    9. // return alert.beginnSheetModal(for:........)
    10. }
    Alles anzeigen
    Danke.
  • Mac & i Test Abo
  • MCDan schrieb:

    Hm, beginSheetModal(for:completionHandler:) verwendet doch einen completionHandler, welche beim Beenden des Sheets aufgerufen wird. ?(
    Die Apple Doku (developer.apple.com/documentat…t/1524296-beginsheetmodal) sieht es vor. Leider bringt mich diese auch nicht wirklich weiter bzgl. meines Vorhabens. :?: Mir fehlt da irgendwie etwas. ?( Wie kann ich dem completionHandler Werte übergeben, die dieser dann der aufrufenden Methode zurückgibt?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von OSXDev ()

  • MCDan schrieb:

    Implementiere doch einfach mal den completionHandler, welcher als Parameter ein NSApplication.ModalResponse hat. Der entsprechende Wert sollte dann zu dem gedrückten Button passen. ;)
    Anbei mal die Umsetzung welche zu folgender Fehlermeldung führt: "Cannot convert value of type '(_) -> Bool' to expected argument type '((NSApplication.ModalResponse) -> Void)?'"


    Quellcode

    1. func dialogHinweisOKCancel(question: String, text: String) -> Bool {
    2. let alert = NSAlert()
    3. alert.messageText = question
    4. alert.informativeText = text
    5. alert.alertStyle = NSAlert.Style.critical
    6. alert.addButton(withTitle: "YES")
    7. alert.addButton(withTitle: "No")
    8. // return alert.runModal() == NSApplication.ModalResponse.alertFirstButtonReturn
    9. return alert.beginSheetModal(for: self.view.window!,
    10. completionHandler: { (modalResponse) -> Bool in
    11. if modalResponse == NSApplication.ModalResponse.alertFirstButtonReturn
    12. {
    13. //NSApplication.ModalResponse.alertFirstButtonReturn
    14. modalResponse = true
    15. }
    16. else
    17. {
    18. //NSApplication.ModalResponse.alertSecondButtonReturn
    19. modalResponse = false
    20. }
    21. })
    22. }
    Alles anzeigen
    ?( :/ ?(

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von OSXDev ()

  • Hat die Code Completion von Xcode Dir den o.a. completionHandler erzeugt?

    Ich kann kein Swift, aber der Parameter sollte laut Doku doch vom Typ NSApplication.ModalResponse sein, oder?

    Für Objective-C lasse ich mir einen completionHandler immer von Xcode durch die Code Completion erzeugen. Ich tippe jetzt einfach mal, dass dies für Swift auch funktionieren sollte. ;)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • OSXDev schrieb:

    Anbei mal die Umsetzung welche zu folgender Fehlermeldung führt: "Cannot convert value of type '(_) -> Bool' to expected argument type '((NSApplication.ModalResponse) -> Void)?'"

    Quellcode

    1. func dialogHinweisOKCancel(question: String, text: String) -> Bool {
    2. return alert.beginSheetModal(f)

    Die Funktion beginSheetModal hat _keinen_ Rückgabewert, aka Void. In Deiner Funktion willst Du aber einen Bool zurückgeben (warum eigentlich?). Das paßt nicht zusammen!
    Das iPhone sagt: "Zum Antworten streichen". Wie? Echt Jetzt? Muß ich erst die Wohnung streichen!?
  • torquato schrieb:

    OSXDev schrieb:

    Anbei mal die Umsetzung welche zu folgender Fehlermeldung führt: "Cannot convert value of type '(_) -> Bool' to expected argument type '((NSApplication.ModalResponse) -> Void)?'"

    Quellcode

    1. func dialogHinweisOKCancel(question: String, text: String) -> Bool {
    2. return alert.beginSheetModal(f)
    Die Funktion beginSheetModal hat _keinen_ Rückgabewert, aka Void. In Deiner Funktion willst Du aber einen Bool zurückgeben (warum eigentlich?). Das paßt nicht zusammen!
    Stimmt. Der von mir ursprünglich verwendete Alert-Aufruf (Methode s. ersten Post) lieferte mir einen Returnwert, welchen ich in der aufrufenden Methode ausgewertet habe. Dies würde ich gerne so beibehalten, sofern dies überhaupt möglich ist?

    Quellcode

    1. func dialogHinweisOKCancel(question: String, text: String) -> Bool {
    2. let alert = NSAlert()
    3. alert.messageText = question
    4. alert.informativeText = text
    5. alert.alertStyle = NSAlert.Style.critical
    6. alert.addButton(withTitle: "YES")
    7. alert.addButton(withTitle: "No")
    8. return alert.runModal() == NSApplication.ModalResponse.alertFirstButtonReturn
    Aufgerufen habe ich diesen so: "ergebnis = dialogHinweisOKCancel(question: "Hinweis", text: "Soll gelöscht werden?")"

    Eigentlich wollte ich nur den Alert direkt an die App koppeln. Das muss doch irgendwie funktionieren, habe es schon bei anderen Apps so gesehen. Nur wenn ich beginnSheetModal verwende, dann ist die Methode wie von Dir bereits angemerkt als VOID deklariert und liefert aus diesem Grund keinen Rückgabewert. Wenn es hierfür keine Lösung gibt bleibt mir wohl nichts anderes übrig als die Auswertung direkt in die beginSheetModal Routine auszulagern bzw. dort vorzunehmen. Hoffe es gibt doch noch einen anderen Lösungsansatz?
  • OSXDev schrieb:

    Stimmt. Der von mir ursprünglich verwendete Alert-Aufruf (Methode s. ersten Post) lieferte mir einen Returnwert, welchen ich in der aufrufenden Methode ausgewertet habe. Dies würde ich gerne so beibehalten, sofern dies überhaupt möglich ist?
    Mit beginSheetModal(for:completionHandler:) ist das nicht möglich. Das hat man dir ja schon gesagt.

    OSXDev schrieb:

    Eigentlich wollte ich nur den Alert direkt an die App koppeln.
    Wenn du den Alert an die App koppeln willst, dann ist runModal() die richtige Methode. Mit beginSheetModal(for:completionHandler:) koppelst du den Alert nur an ein Fenster.

    Übrigens, ist die Beschriftung der Buttons mit „YES“ und „NO“ Windows-Style. Auf einem Mac erwarte ich da sowas wie „Delete“ und „Cancel“
  • MCDan schrieb:

    Alternativ könntest Du der Methode dialogHinweisOKCancel auch einen completionHandler verpassen.
    @'MCDan: Das werde ich mal ausprobieren.

    @'Michael: Gibt es weitere Alternativen zum Aufruf eines Alerts (-Fensters) um eine Bestätigung zu erhalten bzw. abzufragen? Ich habe es aufgrund der Einfachheit mit dem Aufruf eines Alerts realisiert. Könnte mir aber durchaus vorstellen, dass es evtl. noch andere Vorgehensweisen bzw. Methoden gibt?

    Ich habe noch eine weitere Lösung bzgl. des Einsatzes von beginSheetModal(for:completionHandler:), unter Zuhilfenahme einer globalen Variablen, realisiert. Sicherlich sind mir die Nachteile dieser Vorgehensweise bekannt. Aber es ist nunmal auch eine Lösung. Gibt es in Swift eine Möglichkeit eine globale Variable zu kapseln?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von OSXDev ()

  • OSXDev schrieb:

    MCDan schrieb:

    Alternativ könntest Du der Methode dialogHinweisOKCancel auch einen completionHandler verpassen.
    Ich habe noch eine weitere Lösung bzgl. des Einsatzes von beginSheetModal(for:completionHandler:), unter Zuhilfenahme einer globalen Variablen, realisiert. Sicherlich sind mir die Nachteile dieser Vorgehensweise bekannt. Aber es ist nunmal auch eine Lösung. Gibt es in Swift eine Möglichkeit eine globale Variable zu kapseln?

    Dir ist schon bekannt, dass Du in einem completionHandler problemlos auf self zugreifen kannst? Warum willst Du da eine globale Variable verwenden? ?(

    Ich würde es einfach mal mit der Lösung über den completionHandler bei dialogHinweisOKCancel probieren. Da brauchst Du dann weder Properties noch globale Variablen. ;)
  • MCDan schrieb:

    OSXDev schrieb:

    MCDan schrieb:

    Alternativ könntest Du der Methode dialogHinweisOKCancel auch einen completionHandler verpassen.
    Ich habe noch eine weitere Lösung bzgl. des Einsatzes von beginSheetModal(for:completionHandler:), unter Zuhilfenahme einer globalen Variablen, realisiert. Sicherlich sind mir die Nachteile dieser Vorgehensweise bekannt. Aber es ist nunmal auch eine Lösung. Gibt es in Swift eine Möglichkeit eine globale Variable zu kapseln?
    Dir ist schon bekannt, dass Du in einem completionHandler problemlos auf self zugreifen kannst? Warum willst Du da eine globale Variable verwenden? ?(

    Ich würde es einfach mal mit der Lösung über den completionHandler bei dialogHinweisOKCancel probieren. Da brauchst Du dann weder Properties noch globale Variablen. ;)
    Jetzt wo du es erwähnst, fällt es mir auch wieder ein. :D
    Das scheint auch zu funktionieren, aber wie unterbreche ich die weitere Verarbeitung bzw. bis das Alert-Fenster eine Antwort vom Anwender erhält? ?(
  • OSXDev schrieb:

    Das scheint auch zu funktionieren, aber wie unterbreche ich die weitere Verarbeitung bzw. bis das Alert-Fenster eine Antwort vom Anwender erhält? ?(

    Du teilst die Verarbeitung in einzelne Schritte (Methoden) auf. Der erste Schritt (1. Methode) zeigt das Alert-Fenster an. In dem completionHandler zum Alert-Fenster rufst Du dann ggf. den zweiten Schritt (2. Methode) auf.

    Wenn Du jetzt noch nach dem MVC Pattern entwickelst, dann befindet sich der 2. Schritt, also die eigentliche Veränderung der Daten, sogar im Datenmodell. ;)
  • Quellcode

    1. DispatchQueue.main.async(execute: {() -> Void in
    2. //2. Methode
    3. })
    Funktioniert einwandfrei. Nur eine Frage bleibt noch offen. Wenn ich dies richtig interpretiere, dann starte ich mit o.g. Sourcecode einen zweiten Prozess innerhalb des Main-Threads. Also der Alert-Prozess wird in die Main-Queue eingereiht. Dies bedeutet doch auch, dass er unabhängig vom Hauptprozess arbeitet? Oder missverstehe ich hier etwas und wenn dem so ist, wie halte ich die Verarbeitung des Main-Prozess so lange an bis der Alert-Prozess geendet hat?
  • Es gibt nur einen Mail-Thread und der Block wird darin ausgeführt, wenn der Main-Thread Zeit dafür hat. Dies ist allgemein im einem der nächsten Durchläufe der Fall.

    Den Main-Thread solltest Du auf keinen Fall blocken, da dann die App "einfriert" und nach einiger Zeit unter macOS der Beachball erscheint. Unter iOS wird die App dann sogar ggf. vom System beendet.

    Längere Verarbeitungen z.B. sollten immer in einem Asynchron bzw. in einem Background-Thread ausgeführt werden. Background-Threads kannst Du auch problemlos Blocken und darin z.B. auf die Beendigung eines Alert-Sheets warten.

    Die bessere Lösung ist jedoch mehr Event-Driven/Asynchron zu denken und zu programmieren und Aufgaben in einzelne Schritte aufzusplitten und nicht so sehr linear zu denken und zu programmieren. ;)
  • MCDan schrieb:

    ...

    Längere Verarbeitungen z.B. sollten immer in einem Asynchron bzw. in einem Background-Thread ausgeführt werden. Background-Threads kannst Du auch problemlos Blocken und darin z.B. auf die Beendigung eines Alert-Sheets warten.

    Die bessere Lösung ist jedoch mehr Event-Driven/Asynchron zu denken und zu programmieren und Aufgaben in einzelne Schritte aufzusplitten und nicht so sehr linear zu denken und zu programmieren. ;)
    Nun meine Frage lt. ebenfalls wie ich einen Thread pausieren lasse. Wäre prima wenn Du mir dies auch noch mitteilen könntest? ;)

    Was Deinen Vorschlag bzgl. der Umsetzung von Lösungsalgorithmen angeht, muss ich Dir mitteilen, dass die Programmierung sich an den gewählten bzw. umzusetzenden Algorithmus orientieren sollte. Freiheitsgrade sind hierbei nur im Umfeld zugelassen. Alles andere würde evtl. zu Chaos und Mehraufwand führen (mehr Codezeilen -> mehr Rechenzeit -> höhere Fehleranfälligkeit; aber hin- und wieder bessere Lesbarkeit). Fazit, sollte je nach Anwendungsfall entschieden werden. :)
  • Ich kann Dir ein Beispiel in Objective-C geben. Dieses müsstest Du dann selbst nach Swift portieren. Evtl. gibt es bei Swift dann auch andere Möglichkeiten. ;)

    Ich habe gerade eine Beschreibung von Semaphores für Swift gefunden. Schau und lies Dir dies mal durch: medium.com/@valentinkalchev/ho…tch-semaphore-fc98eca55c0

    Dann habe ich mal versucht, dies in Deinem Code einzubauen, so wie ich dies in Objective-C gemacht hätte:

    C-Quellcode

    1. func dialogHinweisOKCancel(question: String, text: String) -> Bool {
    2. let alert = NSAlert()
    3. alert.messageText = question
    4. alert.informativeText = text
    5. alert.alertStyle = NSAlert.Style.critical
    6. alert.addButton(withTitle: "YES")
    7. alert.addButton(withTitle: "No")
    8. // Hier soll der returnCode vom Sheet gespeichert werden
    9. var NSModalResponse returnCode = NSModalResponseCancel
    10. // Semaphore erzeugen
    11. let semaphore = dispatch_semaphore_create(0)
    12. alert.beginSheetModal(for: self.view.window!,
    13. completionHandler: { (modalResponse) in
    14. returnCode = modalResponse
    15. dispatch_semaphore_signal(semaphore)
    16. })
    17. // Auf ein Signal der Semaphore warten
    18. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
    19. // Den returnCode vom Sheet auswerten
    20. if (returnCode == NSApplication.ModalResponse.alertFirstButtonReturn)
    21. return true;
    22. else
    23. return false;
    24. }
    Alles anzeigen
    Evtl. passt die Swift Syntax nicht, da musst Du dann ggf, noch ein wenig nachbessern. ;)

    dialogHinweisOKCancel kannst/darfst Du jetzt nicht mehr im MainThread aufrufen. Ggf. muss beginSheetModal(for:completionHandler:) dann wieder im MainThread ausgeführt werden. Dazu kannst Du dann ein DispatchQueue.main.async() verwenden.

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von MCDan ()