NotificationCenter

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

  • NotificationCenter

    Hallo zusammen,

    ich habe ein kleines Problemchen und hoffe ihr habt eine Lösung / oder einen Hinweis / Ansatz.

    Ich beschäftige mich gerade in einer Test-App mit dem NotificationCenter und möchte folgendes zum laufen bringen bevor ich es auf mein aktuelles Projekt adaptiere.
    Der Aufbau ist, weil es nur ein Sample-Projekt ist, nur ein Tab-Bar Controller mit 2 ViewControllern.

    ViewController 1 ist initial VC. Hier habe ich in der Klasse eine konstante in der ich "NotificationCenter.default" abgelegt habe.
    In der ViewDidLoad habe ich

    Quellcode

    1. let notificationName = Notification.Name("TestNotification")
    2. notificationCenter.addObserver(self, selector: #selector(meineFunktion(_:)), name: notificationName, object: nil)


    und in der Klasse

    Quellcode

    1. @objc func myMethod(_ notification: NSNotification){
    2. print("TEST")
    3. }
    In ViewController2 habe ich in der viewDidLoad nur

    Quellcode

    1. let notificationName = Notification.Name("TestNotification")
    2. notificationCenter.post(name: notificationName, object: nil, userInfo: nil)
    ___

    ___

    Wenn ich nun die App starte passiert nichts, wenn ich auf den zweiten VC wechsel bekomme ich die Ausgabe "TEST" in der Konsole was mir sagt, die @objc-Funktion wurde aufgerufen.
    Wenn ich beide Scripte tausche passiert weder in 1 etwas noch in zwei und ich glaube zu wissen warum.

    Wenn es klappt habe ich den Observer in der ViewDidLoad durch aufrufen des VC "aktiviert" und dieser reagiert dann auf die Notification die in der ViewDidLoad gesendet wird.
    Andersrum schicke ich die Nachricht zwar aber der Observer ist noch gar nicht, durch aufrufen der viewDidLoad "aktiv".

    Wie kann ich das lösen? Ziel soll es sein beim Aufruf von VC1 eine Funktion in VC2 auszuführen.

    Vielen Dank an alle die sich die Mühe machen mir zu schreiben / helfen.

    VG Steffe
  • Steffe schrieb:

    Wenn ich nun die App starte passiert nichts, wenn ich auf den zweiten VC wechsel bekomme ich die Ausgabe "TEST" in der Konsole was mir sagt, die @objc-Funktion wurde aufgerufen.
    Das wundert mich schon, weil Dein Observer die Methode meineFunktion: feuern soll, wenn er eine Benachrichtigung erhält, die Methode aber myMethod: heisst. Ich vermute hier mal einen Tipp- / Copy & Paste-Fehler...

    Steffe schrieb:

    Wie kann ich das lösen? Ziel soll es sein beim Aufruf von VC1 eine Funktion in VC2 auszuführen.
    Ist denn der VC2 schon instanziert bzw. noch nicht wieder freigegeben, wenn VC1 aufgerufen wird (oder meinst Du "erscheint")? Ich könnte mir vorstellen, dass Du hier ein konzeptionelle Problem hast. Wenn beide VCs instanziert sind, funktioniert das o. g. Verfahren auch prinzipiell. Obwohl es einfacher sein könnte, in VC1 eine Referenz auf VC2 zu halten und die Methode dann direkt aufzurufen.

    Notifications machen m. E. nur Sinn, wenn der Absender gar nicht weiss, wer alles seine Nachrichten "abonniert" hat ... zum Beispiel wenn mehrere Fenster auf eine Hintergrundaktualisierung reagieren sollen. Bei einfachen 1:1-Beziehungen empfinde ich sie als Overkill.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Hallo @MyMattes,

    ich habe beim reinkopieren meine auskommentieren Dinge gelöscht um es übersichtlicher zu halten und habe deshalb den Methoden-Namen geändert und das bei einem vergessen - die sind im Code schon identisch. ;) Aber danke für den Hinweis :)

    Ich glaube ich habe die gar nicht instanziert.
    Ich erstelle einen neuen VC stets mit der Vererbung der Klasse "UIViewController" aus Cocoa-Touch.

    Ich meine ich habe was gelesen, dass man das über die AppDelegate machen kann aber das habe ich noch nie gemacht.

    Für mich galt bislang - viewDidLoad wird gerufen wenn der Controller in den Speicher geladen wurde - also beim Aufruf.
    viewWillAppear ist eine Stufe davor ... aber instanziert!? :D

    VG Steffe

    PS: Ich komme ursprünglich aus dem Web bereich ... da haben wir meist solche Probleme nicht :D
  • Steffe schrieb:

    Ich glaube ich habe die gar nicht instanziert.
    Doch, das hast Du sicher ... Du weisst es nur vielleicht nicht :D

    Mit "Instanzieren" ist das Erstellen einer konkreten - physischen - Instanz aus einer Objektklasse gemeint (die natürlich eine Unterklasse sein kann). Also quasi das Anlegen des Objektes mit entsprechenden Allokieren von Speicher, Initialisieren der Instanz-Variablen etc. Das kann explizit in Deinem Code erfolgen, aber auch implizit z. B. durch ein Storyboard.

    Ich meine nur, dass VC2 noch nicht "existiert", wenn VC1 die Nachricht schickt. Oder VC2 kann auch schon wieder "abgeräumt" worden sein, wenn Du aus ihm - z. B. über einen NavigationController - zurück zu VC1 springst. Verhindern lässt sich letzteres eigentlich nur, indem Du eine starke Referenz auf VC2 hältst, also z. B. VC1 eine "strong" Variable hat, die auf VC2 verweist. Dann kannst Du mit der aber auch direkt eine Methode von VC2 aufrufen...

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.

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

  • Hallo nochmal @MyMattes,

    danke für den Tip mit dem instanzieren.

    Mit etwas Google-Recherche habe ich die Lösung gefunden.
    Ich hatte im Test-Projekt keine extra Klasse für den TabBarController.

    Diesen erstellt und folgenden Code eingefügt:

    Quellcode

    1. let vc = self.viewControllers![1] as! ViewController2
    2. NotificationCenter.default.addObserver(vc, selector: #selector(vc.meineFunktion(_:)), name: NSNotification.Name(rawValue: "TestNotification"), object: nil)
    Jetzt feuere ich in ViewController1 die Notification ab die in ViewController2 das ausführen der Funktion bewirkt ohne das ViewController2 aufgerufen werden
    muss.

    Vielen Dank nochmals für deine Hilfe! Manchmal reicht ja auch ein Stichwort um sich selbst eine Lösung herzuleiten - es muss ja nicht immer - wie leider viele denken - die fertige Lösung sein.

    VG Steffe
  • Steffe schrieb:


    Mit etwas Google-Recherche habe ich die Lösung gefunden.
    Ich hatte im Test-Projekt keine extra Klasse für den TabBarController.
    Nur als Ergänzung / Erläuterung: Diese Info zu Deiner View-Hierarchie fehlte, damit besteht zumindest nicht die Problematik, dass VC2 im Hintergrund freigegeben / "abgeräumt" wird und die Notifications in's Leere läuft...

    MyMattes schrieb:

    Verhindern lässt sich letzteres eigentlich nur, indem Du eine starke Referenz auf VC2 hältst, also z. B. VC1 eine "strong" Variable hat, die auf VC2 verweist.
    ... denn der TabBarController hält starke Referenzen auf die ViewController in seinem Array-Property viewControllers, so dass diese nicht vorzeitig freigegeben werden können (retainCount > 0).

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Hallo @MyMattes,

    vielen Dank nochmals - auch für die Erläuterung.

    Ich habe gerade versucht die Schnipsel von der Test-App in meine aktuelle Entwicklungsumgebung der App einzubinden.

    Ich habe jetzt das Problem, dass ich den ViewController, der noch nicht gezeigt wurde in einem NavigationController liegen habe.

    Der Aufbau ist also:
    TabBarController
    -NavigationController
    --ViewController

    Wie kann ich diesen nun instanzieren?

    Bislang war es ein TabBarController mit 2 VC's und der Code sieht so aus ( in der Test-App )

    Quellcode

    1. let vc = self.viewControllers![1] as! ViewController2
    2. NotificationCenter.default.addObserver(vc, selector: #selector(vc.notified(_:)), name: NSNotification.Name(rawValue: "NotificationSample"), object: nil)
    Jetzt müsste irgendwie der NavigationController ( der eine Storyboard-ID aber keine eigene Klasse hat ) dazwischen damit der "Pfad" stimmt, oder!?

    VG Steffe
  • Jetzt sind wir wieder bei konzeptionellen Problemen: Wenn VC1 eine Info an VC2 geben soll, kann es zwei Situationen geben:
    1. VC2 existiert - z. B. weil er bereits als ViewController eines UITabbarControllers erzeugt wurde - und Du kannst ihm eine Notification schicken. Oder sonstwie eine Methode in ihm aufrufen.
    2. VC2 existiert noch nicht, z. B. weil dazwischen ein UINavigationController sitzt, dessen Sub-VCs erst im Rahmen der Navigation per Segues o. ä. erzeugt werden.
    Natürlich kannst Du versuchen, den zweiten Fall krampfhaft in den ersten zu überführen, indem Du beispielsweise ein Erstellen (und Halten) der Sub-VCs erzwingst ... das Thema der o. g. starken Referenzen. Das ist aber m. E. unsauber und fällt Dich kurz über lang vor die Füße.

    Sauberer wäre, die Information aus VC1 so abzulegen, das VC2 sie immer honoriert, entweder sofort oder wenn er erzeugt wird. Möglichkeiten wären hier Properties des AppDelegates, UserDefaults, eine Datenhaltungs-Schicht uvm. Das könnte mit einer Notification kombiniert werden, die nur auf die Datenänderung hinweist.

    Aber viele Wege führen nach Rom :)

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Hello again @MyMattes,

    dann würde ich mich sehr über ein Codebeispiel freuen.
    AppDelegate würde ich dann bevorzugen.

    Ich habe es jetzt erstmal folgendermaßen gemacht:
    Die Funktion einfach aus dem noch nicht geladenen VC in den ersten VC kopiert.
    Da ich eine MVC-Struktur nutze und auch in der Funktion eine API innerhalb der App geschrieben habe kann ich es so machen.
    Auch wenn ich es eben wegen dem DRY-Prinzip als unsauber erachte.
    DRY-Prinzip hin oder her .... ansonsten trete ich ohne Beispiel auf der Stelle.


    VG und abermals Danke!
  • Steffe schrieb:

    dann würde ich mich sehr über ein Codebeispiel freuen.
    AppDelegate würde ich dann bevorzugen.
    Wer mich hier im Forum kennt, weiss, dass ich mich mit Code-Beispielen immer schwer tue: Ich bin ein Fan von „Hilfe zur Selbsthilfe“ und ein AppDelegate-Property ist codeseitig keine große Herausforderung.

    Aber wenn Du oben sagst, die App nach MVC strukturiert zu haben, wäre eine Abbildung in der Datenhaltung eigentlich konsequent: Alle VCs nutzen (bidirektional) Informationen des Models zur Steuerung ihrer Views. Also sollten alle VCs automatisch auf Änderungen des Models reagieren (z. B. durch Lesen im init und Notifications), und VC1 könnte einfach ein entsprechendes Attribut setzen … das VC2 dann nutzt.

    Meiner Meinung nach sauberer, als das AppDelegate-Singleton zur Definition quasi globaler Variablen zu nutzen.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.