OSX Storyboard Menubaritems rufen verschiedene ViewController direkt aus dem Menu auf / Darstellung in einem Window

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

  • OSX Storyboard Menubaritems rufen verschiedene ViewController direkt aus dem Menu auf / Darstellung in einem Window

    Hallo,

    ich hoffe mir wird hier geholfen. Bin zum ersten Mal in einem Forum angemeldet. Da ich i.A. Zeit habe versuche ich mich an meiner ersten App für OSX. Für die Umsetzung habe ich mich für Swift 3 entschieden. Installiert habe ich Xcode 8.3.2. Ärgerlicherweise habe ich mir ein Swiftbuch für die Version 2.2 gekauft und da funktioniert nicht wirklich alles wie es beschrieben wird.

    Ein neues Projekt anlegen ist ja noch relativ einfach. Gelöst habe ich dies mit folgenden Schritten:
    - macOS / Cocoa Applikation / Langauge: Swift

    Im Storyboard sind nun ein Window und ein ViewController zu sehen - soweit ist alles i.O.. Nun habe ich einen weiteren ViewController hinzugefügt und damit beginnt nun meine Problem. Um diesen anzusprechen habe ich im ersten ViewController ein Push Button angelegt und diesen mit dem zweiten ViewController verbunden. Nach dem starten der App kann ich durch betätigen der Push Buttons den zweiten ViewController anzeigen lassen. Prima :)

    Und nun habe ich versucht ein Menüunterpunkt mit dem zweiten ViewController zu verbinden, hierbei habe ich die Option SHOW gewählt. CUSTOM liefert einen Fehler und MODAL zeigt keinen Unterschied zur OPTION SHOW also habe ich SHOW gewählt. Liest sich auch besser. ;)
    Nach dem starten der App kommt es nun zu folgendem Phänomen; wenn der Menüunterpunkt (Zeige 2. VC) betätige, dann wird mir ein neues Fenster mit dem zweiten ViewController angezeigt. Es werden keine Fehlermeldungen ausgegeben. Also - es läuft problemlos, nur ist dieses Ergebnis von mir so nicht gewünscht gewesen. Ich dachte es wird wie der erste ViewController von oben nach unten im Hauptfenster eingeblendet. ;(

    Es wäre toll wenn mir jemand mitteilen könnte wo mein Gedankenfehler liegt und wie ich diese Verbindung im Storyboard anlegen muss damit der zweite ViewController ebenfalls im Hauptfenster erscheint.

    Danke schon mal vorab.

    VG Nicole
  • Hallo Nicole,

    willkommen im Forum (oder überhaupt in einem Forum :D )!

    Ich lasse mich gerne von anderen korrigieren, aber meines Wissens nach lässt sich Dein Vorhaben nicht ohne Programmcode erreichen. Ich würde mir eine Art Container-View erstellen, zu der ich die (auszutauschende) View A als Subview hinzufügen würde. Der Menü-Eintrag würde dann eine Methode des Controllers der Container-View triggern, in der die View A durch eine zweite View B ausgetauscht würde, z. B. mittels replaceSubView(_:with:).

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MCDan schrieb:

    Der Beschreibung nach zu urteilen, müsste die gesuchte Methode wohl beginSheet(_:completionHandler:) sein.

    Erstelle Dir im AppDelegate eine IBAction, die Du mit dem Menüpunkt verbindest. In der Methode kannst Du dann den gewünschten ViewController über das Storyboard laden und per beginSheet(_:completionHandler:) anzeigen.
    Wusste nicht, dass mir ein Satz soviel Kopfzerbrechen bereiten kann. ?(

    Also in AppDelegate habe ich eine IBAction eines Menüunterpunktes angelegt. Doch nun bin ich doch ziemlich ratlos. Also in der IBAction wollte ich mit

    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let viewController = storyboard.instantiateViewController(withIdentifier :"ViewControllerTwo") as! UIViewController

    den ViewController laden. Doch ich erhalte einen Fehler, dessen Hinweis lt.:"Use of unresolved identifier 'UIStoryboard'" und nun grüble und grüble und grüble und komm nicht weiter.

    Stimmt mein Sourcecode soweit oder liege ich hier völlig daneben?

    Danke @MCDan, danke auch @MyMattes
  • Ohhhh :rolleyes:

    Naja ich schrieb ja schon, dass das Buch nicht so gut ist. Oder aber ich habe es einfach überlesen. ^^

    Also gilt generell folgende Regel: Methoden mit NS.... gelten für OSX und Methoden mit UI... für IOS. Schon wieder etwas gelernt. :)

    Kannst Du mir aktuelle Quellen nennen, die ich als Referenzwerke nutzen kann oder mir gar ein wirklich gutes Buch nennen. Noch einmal daneben greifen möchte ich nicht.

    Ich habe meinen Code nun folgendermaßen angepasst, in der Hoffnung er richtig zu machen:
    let storyboard = NSStoryboard(name: "Main", bundle: nil)
    let mainWindowStoryboard = storyboard.instantiateController(withIdentifier: "ViewControllerTwo") as! NSViewController

    @MCDan: Nun hast Du mir mitgeteilt, dass ich den ViewController mittels der beginSheet Methode anzeigen kann. Vielleicht bin ich ja auch schon zu müde aber könntest Du mir die Codezeile mitteilen? Ich ernte hier nur Fehler! Diese Methode scheint es wohl so nicht mehr zu geben oder was wahrscheinlicher ist, ich finde da nicht den richtigen Einstieg.

    Vielen Dank.

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

  • Da beginSheet(_:completionHandler:) ein NSWindow als Parameter benötigt, brauchst Du natürlich ein NSWindow und nicht nur einen NSView.

    Lege Dir also im Storyboard ein Window an und lade es über instantiateController(withIdentifier:).

    Wenn Du im AppDelegate eine Property auf das das MainWindow hast, dann kannst Du das geladene Window per

    Quellcode

    1. self.mainWindow.begin(_:newWindow completionHandler:nil);
    anzeigen (wenn die Swift Syntax passt :D ).

    Evtl. ist ja das macOS Development for Beginners bei raywenderlich.com etwas für Dich. Dort gibt es auch noch weitere Tutorials für macOS.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von MCDan () aus folgendem Grund: Links zu Tutorials ergänzt. ;)

  • MCDan schrieb:

    Da beginSheet(_:completionHandler:) ein NSWindow als Parameter benötigt, brauchst Du natürlich ein NSWindow und nicht nur einen NSView.

    Lege Dir also im Storyboard ein Window an und lade es über instantiateController(withIdentifier:).

    Wenn Du im AppDelegate eine Property auf das das MainWindow hast, dann kannst Du das geladene Window per

    Quellcode

    1. self.mainWindow.begin(_:newWindow completionHandler:nil);

    anzeigen (wenn die Swift Syntax passt :D ).

    Evtl. ist ja das macOS Development for Beginners bei raywenderlich.com etwas für Dich. Dort gibt es auch noch weitere Tutorials für macOS.
    Hallo MCDan,

    Deinem Quellcode ist ein Semikolon angehängt?! Handelt es sich hierbei nicht um die Syntax von Objective-C? :D

    Bei der Projektanlage wurde mir bereits ein Window und ein ViewController automatisch erzeugt. Weshalb also alles löschen und von Hand anlegen oder verstehe ich Dich hier nicht richtig? ?(

    Also eine Property (@property (weak) IBOutlet NSWindow *window ) kann ich mittlerweile auch anlegen nur wo soll ich diese anlegen in AppDelegate.swift? Und wie verbinde ich diese mit dem bereits vorhandenen MainWindow?
    -> gerade nachgelesen: Eine Property kann ja alles mögliche sein! Also auch eine Funktion; sind größtenteils identisch mit dem Verhalten von Strukturen.

    Fazit: Ist es richtig, dass ich eine Funktion in Appdelegate.swift anlegen muss, welche mir den Pointer auf das mainWindow ausliest?

    Soweit ich die Literatur richtig verstanden habe, ist in Swift doch bereits eine BINDUNG angelegt? Einfach mit gedrückter CTRL- und Maus-Taste funktioniert dies nicht?

    So schwer hätte ich mir mein Vorhaben nicht vorgestellt. :huh: Danke für die Quellenhinweise. Einen Hinweis für mein Problem habe ich da im Ansatz zwar gefunden aber eine Lösung nicht wirklich. Hoffe Du oder jemand hier kann mich dabei unterstützen die Lösung zu erarbeiten. Hoffe meine Fragen sind nicht allzu laienhaft gestellt. :rolleyes:

    Mit einem TabViewController habe ich bereits auch schon experimentiert. Funktioniert ebenfalls prima, nur ist dies eben kein Aufruf via MenubarItem so wie ich mir es vorgenommen habe zu realisieren und bekanntlich lernt man am schnellsten durch ausprobieren. ^^ Ich hoffe dennoch auf regen Beistand.

    Sorry - ich frage so viel nach, weil ich eben eine Anfängerin bin und mir das verwendete Vokabular nicht eben geläufig ist. Aber ich lerne schnell. :saint:

    So mein Sourcecode in AppDelegate.swift:

    In Zeile 5 erhalte ich einen Fehlerhinweis:"Value of type 'NSWindow' has no member 'window'."

    Mir ist schon klar was die Fehlermeldung aussagt. Jedoch weiss ich einfach nicht weiter. Die IBAction habe ich via FirstResponder mit dem MenubarItem verbunden. Kann mir jemand weiterhelfen?

    Quellcode

    1. @IBAction func showVC2(_ sender: AnyObject)
    2. {
    3. let storyboard = NSStoryboard(name: "Main", bundle: nil)
    4. let secondWindowVC = storyboard.instantiateController(withIdentifier: "VC2") as! NSWindow
    5. if let mainWindow = secondWindowVC.window
    6. {
    7. mainWindow.beginSheet(secondWindowVC, completionHandler: nil)
    8. let application = NSApplication.shared()
    9. application.runModal(for: mainWindow)
    10. mainWindow.close()
    11. }
    12. }
    Alles anzeigen

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

  • Hallo,

    Vielleicht kann mir ja doch jemand helfen. :?:

    Meine AppDelegate Klasse sieht nun so aus (s. Quellcode).

    In der Zeile: self.mainWindow?.beginSheet(secondWindowVC, completionHandler: nil)
    wird als erster Parameter ein NSWindow verlangt. Jedoch weiss ich nicht, was ich hier eintragen muss bzw. welche Referenz hier erstellt werden muss?

    AppDelegate.swift:

    Quellcode

    1. import Cocoa
    2. @NSApplicationMain
    3. class AppDelegate: NSObject, NSApplicationDelegate {
    4. ...
    5. // Zugriff auf den View-Controller des Fensters
    6. var mainVC: ViewController?
    7. // Action-Mehtode für einen Menüeintrag
    8. @IBAction func operateWithVC1(_ sender: NSMenuItem)
    9. {
    10. mainVC?.mylabel.stringValue = "Yeeeeaaaahhhh"
    11. }
    12. // Show Sheet
    13. @IBAction func showSheetVC(_ sender: NSMenuItem)
    14. {
    15. let storyboard = NSStoryboard(name: "Main", bundle: nil)
    16. let secondWindowVC = storyboard.instantiateController(withIdentifier: "VC2") as! NSViewController
    17. self.mainWindow?.beginSheet(secondWindowVC, completionHandler: nil)
    18. }
    19. ...
    20. }
    Alles anzeigen
    Wäre wirklich super wenn mir jemand bei der Lösung helfen könnte. :saint:

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von nicole ()

  • Wie schon gesagt, musst Du aus dem SecondViewController einen WindowController machen. Dupliziere also einfach mal den WindowController und nenne diesen z.B. SecondWindowController.

    Bei storyboard.instantiateController() in showSheetVC() musst Du dann den Identifier des SecondWindowController verwenden. Dann sollte es funktionieren.
  • Guten Morgen MCDan,

    wie von Dir vorgeschlagen habe ich den vorhandenen SecondViewController gelöscht und den vorhandenen WindowController dupliziert und als StoryboardID SecondWindowController verwendet.

    Folgende Zeile habe ich anpassen müssen, da ich sonst eine Fehlermeldung erhalte:

    Quellcode

    1. let secondWindowVC = storyboard.instantiateController(withIdentifier: "SecondViewController") as! NSWindow
    also "as! NSViewController" wurde durch "as! NSWindow" ersetzt.

    Starten kann ich die App nun ohne Fehlermeldung, jedoch erhalte ich bei der MenüItem-Auswahl "Show SecendViewController as Sheet" folgenden Fehlernachricht: "Could not cast value of type 'NSWindowController' (0x7fffdb3ddfd0) to 'NSWindow' (0x7fffdb3d21d0).


    2017-07-23 11:49:16.486266+0200 WinVCMenu[1594:33375] Could not cast value of type 'NSWindowController' (0x7fffdb3ddfd0) to 'NSWindow' (0x7fffdb3d21d0)."

    Wenn ich jedoch die Anpassung s.o. durch "as! NSWindowController" ersetzte, dann erhalte ich beim kompilieren folgende Fehlermeldung:
    "Cannot convert value of type 'NSController' to expected argument type 'NSWindow'".

    Wie kann ich dies beheben?

    Vielen Dank für Deine Unterstützung, MCDan.
  • Dachte ich mir schon, dass mir da eine Verbindung vom NSWindowController zum NSWindow fehlt. ;)

    Nun sieht das Ganze so aus wie Du es mir so prima beschrieben hast:

    Quellcode

    1. let secondWindowController = storyboard.instantiateController(withIdentifier: "SecondViewController") as! NSWindowController
    2. let rootWindow = secondWindowController.window
    3. self.mainWindow?.beginSheet(rootWindow!, completionHandler: nil)

    Ach so, ich erhielt den Hinweis, dass das kopierte NSWindow keinen NSViewController enthält; dieser jedoch benötigt wird. Also habe ich im Storyboard einen ViewController hinzugefügt und diesen mit dem kopierten NSWindow (SecondWindow) verlinkt. Die Verlinkung entspricht nun dem Urspungswindow samt seinem ViewController.

    Also nun wird es wirklich sehr schwer für mich, da weder beim Kompilieren noch während der Laufzeit ein Fehler auftritt.

    Die App startet, nur passiert überhaupt nix.
    Was habe ich nun schon wieder aus Unwissenheit nicht berücksichtigt? :/
  • Du lagst mit Deiner Vermutung vollkommen richtig. Die self.mainWindow Variable hat den Wert nil.

    Ich habe nun viele Wege ausprobiert um hier einen Bezug bzw. einen Wert zu erhalten. War aber nicht von Erfolg gekrönt.

    Mir fehlt da der Zusammenhang. MainVC hat doch mit der selben Vorgehensweise bzgl. Deklaration auch einen Wert und die Aktionen welche ich mit mainVC durchführen funktionieren tadellos.

    Wo übersehe ich etwas und vor allem was übersehe ich hier? Nehme mal an, dass mir eine Bindung/Verlinkungen fehlt?

    :) Habe die Lösung gefunden. Manchmal ist es doch sehr hilfreich sich die Verknüpfungen mal aufzuzeichnen. :D

    Nun funktioniert es einwandfrei. :thumbsup:

    Eine letzte Frage zu diesem Thema habe ich noch und zwar möchte ich einen BUTTON genannt CLOSE nutzen um das angezeigte Window zu schließen. Die Aktion hier nennt sich DISMISS ( glaube ich) nur wird mir diese nicht angezeigt.
    Wie muss ich vorgehen?

    Und noch ein sehr großes DANKESCHÖN an MCDan, dass Du so geduldig mit mir bist. :thumbsup:

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

  • Da das Window als Sheet per beginSheet(_:completionHandler:) angezeigt wird, solltest Du endSheet(_:) oder endSheet(_:returnCode:) verwenden, um das Sheet zu schließen.

    Die Methode kannst Du als IBAction im SecondWindowController definieren und entsprechend im IB mit dem Close Button verbinden.

    Über das sheetParent Property vom Window des SecondWindowController erhältst Du das Window, in dem das Sheet angezeigt wird. Bei diesem Window musst Du dann endSheet(_:) oder endSheet(_:returnCode:) aufrufen.

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

  • Mir war zu Beginn auch nicht wirklich klar, dass ich nicht direkt einen ViewController (also ohne NSWindow) als Sheet einblenden kann, obwohl ich ein MenubarItem direkt mit einem VC verbinden und aufrufen kann. ;)

    Innerhalb von Storyboards muss immer die Kombination NSWindow und NSViewController zu Einsatz kommen. Dies habe ich in meinem super tollen Buch in einem Nebensatz beim nochmaligem durchlesen gefunden. Wenn dies in einer Merkhilfebox - Hinweis - und nicht als nebensächliche Anmerkung kenntlich gewesen wäre, währen mir viele meiner anfänglichen Irritationen erspart geblieben. Naja, es kann auch an mir gelegen haben - war vielleicht einfach schon zu spät oder zu früh als mein Kopf über dem Buch hing. :rolleyes:

    Nun habe ich also noch das Problem mit dem Schliessen des Sheet via Button. Vielleicht hat noch jemand einen Rat/Lösung die er uns mitteilen kann?
    Wäre wirklich toll wenn wir dies noch zu Ende bringen könnten. :?:

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