Wie erhalte ich bei einer Ansicht den viewController?
141
Ich habe einen Zeiger auf a UIView. Wie greife ich darauf zu UIViewController? [self superview]ist ein anderer UIView, aber nicht der UIViewController, richtig?
Grundsätzlich versuche ich, viewWillAppear meines viewControllers aufzurufen, da meine Ansicht verworfen wird. Die Ansicht wird von der Ansicht selbst verworfen, die ein Tippen erkennt und [self removeFromSuperview] aufruft. Der viewController ruft nicht viewWillAppear / WillDisappear / DidAppear / DidDisappear selbst auf.
Mahboudz
Ich meinte, ich versuche viewWillDisappear aufzurufen, da meine Ansicht verworfen wird.
Ja, das superviewist die Ansicht, die Ihre Ansicht enthält. Ihre Ansicht sollte nicht genau wissen, um welchen Ansichts-Controller es sich handelt, da dies gegen die MVC-Prinzipien verstoßen würde.
Der Controller hingegen weiß, für welche Ansicht er verantwortlich ist ( self.view = myView), und normalerweise delegiert diese Ansicht Methoden / Ereignisse für die Verarbeitung an den Controller.
In der Regel sollten Sie anstelle eines Zeigers auf Ihre Ansicht einen Zeiger auf Ihren Controller haben, der wiederum entweder eine Steuerlogik ausführen oder etwas an seine Ansicht übergeben kann.
Ich bin mir nicht sicher, ob dies gegen die MVC-Prinzipien verstoßen würde. Zu jedem Zeitpunkt verfügt eine Ansicht nur über einen Ansichtscontroller. In der Lage zu sein, darauf zuzugreifen, um eine Nachricht an sie zurückzugeben, sollte eine automatische Funktion sein, nicht eine, an der Sie arbeiten müssen, um dies zu erreichen (indem Sie eine Eigenschaft hinzufügen, um den Überblick zu behalten). Über Ansichten könnte man dasselbe sagen: Warum müssen Sie wissen, wer Sie sind? Oder ob es andere Geschwisteransichten gibt. Es gibt jedoch Möglichkeiten, diese Objekte zu erhalten.
Mahboudz
Sie haben etwas Recht mit der Ansicht, wissen um die übergeordnete Ansicht, es ist keine sehr klare Entwurfsentscheidung, aber es ist bereits festgelegt, einige Aktionen auszuführen, indem Sie direkt eine Superview-Elementvariable verwenden (übergeordneten Typ überprüfen, vom übergeordneten Element entfernen usw.). Nachdem ich kürzlich mit PureMVC gearbeitet habe, bin ich etwas wählerischer in Bezug auf Designabstraktion geworden :) Ich würde parallel zwischen den UIView- und UIViewController-Klassen des iPhones und den View- und Mediator-Klassen von PureMVC arbeiten - die View-Klasse muss dies meistens nicht Kenntnisse über den MVC-Handler / die MVC-Schnittstelle (UIViewController / Mediator).
Dimitar Dimitrov
9
Schlüsselwort: "am meisten".
Glenn Maynard
278
Aus der UIResponderDokumentation für nextResponder:
Die UIResponder-Klasse speichert oder setzt den nächsten Responder nicht automatisch, sondern gibt standardmäßig nil zurück. Unterklassen müssen diese Methode überschreiben, um den nächsten Responder festzulegen. UIView implementiert diese Methode, indem es das UIViewController-Objekt zurückgibt, das es verwaltet (falls vorhanden) oder die Übersicht (falls nicht) . UIViewController implementiert die Methode, indem es die Übersicht seiner Ansicht zurückgibt. UIWindow gibt das Anwendungsobjekt zurück und UIApplication gibt null zurück.
Wenn Sie also eine Ansicht so lange rekursieren, nextResponderbis sie vom Typ ist UIViewController, haben Sie den übergeordneten viewController einer Ansicht.
Beachten Sie, dass möglicherweise noch kein übergeordneter Ansichtscontroller vorhanden ist. Aber nur, wenn die Ansicht nicht Teil der Ansichtshierarchie einer viewController-Ansicht ist.
Nur eines: Wenn Sie sich Sorgen über die Verschmutzung von Kategorien machen, definieren Sie diese einfach als statische Funktion und nicht als Makro. Auch die brutale Typografie ist gefährlich, und außerdem ist das Makro möglicherweise nicht korrekt, aber nicht sicher.
Mojuba
Das Makro funktioniert, ich benutze die Makroversion nur persönlich. Ich starte viele Projekte und habe einen Header, den ich einfach überall mit einer Reihe dieser Makro-Dienstprogrammfunktionen ablege. Spart mir Zeit. Wenn Sie keine Makros mögen, können Sie sie in eine Funktion umwandeln, aber statische Funktionen scheinen mühsam zu sein, da Sie dann in jede Datei, die Sie verwenden möchten, eine einfügen müssen. Scheint, als ob Sie stattdessen eine nicht statische Funktion in einem Header deklarieren und in einer .m irgendwo definieren möchten?
mxcl
Schön, einige Delegierte oder Benachrichtigungen zu vermeiden. Vielen Dank!
Ferran Maylinch
Sie sollten es zu einer Erweiterung von machen UIResponder;). Sehr erbaulicher Beitrag.
ScottyBlades
31
@ andrey Antwort in einer Zeile (getestet in Swift 4.1 ):
parentViewControllerkann nicht definiert werden, publicwenn sich die Erweiterung in derselben Datei befindet wie Sie, UIViewSie können sie festlegen fileprivate, sie wird kompiliert, funktioniert aber nicht! 😐
Es funktioniert gut. Ich habe dies für die Aktion "Zurück" in der .xib-Datei des allgemeinen Headers verwendet.
McDonal_11
23
Nur zu Debug-Zwecken können Sie _viewDelegateAnsichten aufrufen , um deren Ansichts-Controller abzurufen. Dies ist eine private API, daher nicht sicher für den App Store, aber zum Debuggen ist sie nützlich.
Andere nützliche Methoden:
_viewControllerForAncestor- Holen Sie sich den ersten Controller, der eine Ansicht in der Übersichtskette verwaltet. (danke n00neimp0rtant)
_rootAncestorViewController - Holen Sie sich den Ahnen-Controller, dessen Ansichtshierarchie derzeit im Fenster festgelegt ist.
Genau deshalb bin ich auf diese Frage gekommen. Anscheinend macht 'nextResponder' dasselbe, aber ich schätze die Einsicht, die diese Antwort bietet. Ich verstehe und mag MVC, aber das Debuggen ist ein anderes Tier!
mbm29414
4
Es scheint, dass dies nur in der Hauptansicht des View Controllers funktioniert, nicht in Unteransichten. _viewControllerForAncestorDurchläuft die Übersichten, bis die erste gefunden wird, die zu einem Ansichts-Controller gehört.
n00neimp0rtant
Danke @ n00neimp0rtant! Ich stimme dieser Antwort zu, damit die Leute Ihren Kommentar sehen.
Eyuelt
Aktualisieren Sie die Antwort mit weiteren Methoden.
Leo Natan
1
Dies ist beim Debuggen hilfreich.
Evanchin
10
Um einen Verweis auf UIViewController mit UIView zu erhalten, können Sie UIResponder (eine Superklasse für UIView und UIViewController) erweitern, die es ermöglicht, die Responderkette zu durchlaufen und so UIViewController zu erreichen (andernfalls wird Null zurückgegeben).
Ich denke, Sie können den Tipp an den View Controller weitergeben und ihn damit umgehen lassen. Dies ist ein akzeptablerer Ansatz. Für den Zugriff auf einen Ansichtscontroller aus seiner Ansicht sollten Sie einen Verweis auf einen Ansichtscontroller beibehalten, da es keinen anderen Weg gibt. In diesem Thread kann es hilfreich sein:
Zugriff auf den View Controller von einer Ansicht aus
Wenn Sie eine Handvoll Ansichten haben und eine alle Ansichten schließt und Sie viewWillDisappear aufrufen müssen, ist es für diese Ansicht nicht einfacher, den Abgriff zu erkennen, als den Abgriff an den View Controller zu übergeben und den View Controller überprüfen zu lassen mit all den Ansichten, um zu sehen, welche angezapft wurde?
Leider ist dies nur möglich, wenn Sie die Ansicht in Unterklassen unterteilen und ihr eine Instanzeigenschaft oder ähnliches bereitstellen, in der die Referenz des Ansichtscontrollers gespeichert wird, sobald die Ansicht zur Szene hinzugefügt wird ...
In den meisten Fällen ist es sehr einfach, das ursprüngliche Problem dieses Beitrags zu umgehen, da die meisten View-Controller dem Programmierer, der für das Hinzufügen von Unteransichten zur ViewController-Ansicht verantwortlich war, bekannte Einheiten sind ;-) Deshalb denke ich, dass Apple nie die Mühe gemacht, diese Eigenschaft hinzuzufügen.
Antworten:
Ja, das
superview
ist die Ansicht, die Ihre Ansicht enthält. Ihre Ansicht sollte nicht genau wissen, um welchen Ansichts-Controller es sich handelt, da dies gegen die MVC-Prinzipien verstoßen würde.Der Controller hingegen weiß, für welche Ansicht er verantwortlich ist (
self.view = myView
), und normalerweise delegiert diese Ansicht Methoden / Ereignisse für die Verarbeitung an den Controller.In der Regel sollten Sie anstelle eines Zeigers auf Ihre Ansicht einen Zeiger auf Ihren Controller haben, der wiederum entweder eine Steuerlogik ausführen oder etwas an seine Ansicht übergeben kann.
quelle
Aus der
UIResponder
Dokumentation fürnextResponder
:Wenn Sie also eine Ansicht so lange rekursieren,
nextResponder
bis sie vom Typ istUIViewController
, haben Sie den übergeordneten viewController einer Ansicht.Beachten Sie, dass möglicherweise noch kein übergeordneter Ansichtscontroller vorhanden ist. Aber nur, wenn die Ansicht nicht Teil der Ansichtshierarchie einer viewController-Ansicht ist.
Swift 3 und Swift 4.1 Erweiterung:
Swift 2-Erweiterung:
Ziel-C-Kategorie:
Dieses Makro vermeidet Verschmutzung durch Kategorien:
quelle
UIResponder
;). Sehr erbaulicher Beitrag.@ andrey Antwort in einer Zeile (getestet in Swift 4.1 ):
Verwendung:
quelle
parentViewController
kann nicht definiert werden,public
wenn sich die Erweiterung in derselben Datei befindet wie Sie,UIView
Sie können sie festlegenfileprivate
, sie wird kompiliert, funktioniert aber nicht! 😐Nur zu Debug-Zwecken können Sie
_viewDelegate
Ansichten aufrufen , um deren Ansichts-Controller abzurufen. Dies ist eine private API, daher nicht sicher für den App Store, aber zum Debuggen ist sie nützlich.Andere nützliche Methoden:
_viewControllerForAncestor
- Holen Sie sich den ersten Controller, der eine Ansicht in der Übersichtskette verwaltet. (danke n00neimp0rtant)_rootAncestorViewController
- Holen Sie sich den Ahnen-Controller, dessen Ansichtshierarchie derzeit im Fenster festgelegt ist.quelle
_viewControllerForAncestor
Durchläuft die Übersichten, bis die erste gefunden wird, die zu einem Ansichts-Controller gehört.Um einen Verweis auf UIViewController mit UIView zu erhalten, können Sie UIResponder (eine Superklasse für UIView und UIViewController) erweitern, die es ermöglicht, die Responderkette zu durchlaufen und so UIViewController zu erreichen (andernfalls wird Null zurückgegeben).
quelle
Der schnelle und generische Weg in Swift 3:
quelle
Wenn Sie mit dem Code nicht vertraut sind und ViewController finden möchten, der der angegebenen Ansicht entspricht, können Sie Folgendes versuchen:
In den meisten Fällen erhalten Sie UIView, aber von Zeit zu Zeit gibt es eine UIViewController-basierte Klasse.
quelle
Ich denke, Sie können den Tipp an den View Controller weitergeben und ihn damit umgehen lassen. Dies ist ein akzeptablerer Ansatz. Für den Zugriff auf einen Ansichtscontroller aus seiner Ansicht sollten Sie einen Verweis auf einen Ansichtscontroller beibehalten, da es keinen anderen Weg gibt. In diesem Thread kann es hilfreich sein: Zugriff auf den View Controller von einer Ansicht aus
quelle
Mehr typsicheren Code für Swift 3.0
quelle
Leider ist dies nur möglich, wenn Sie die Ansicht in Unterklassen unterteilen und ihr eine Instanzeigenschaft oder ähnliches bereitstellen, in der die Referenz des Ansichtscontrollers gespeichert wird, sobald die Ansicht zur Szene hinzugefügt wird ...
In den meisten Fällen ist es sehr einfach, das ursprüngliche Problem dieses Beitrags zu umgehen, da die meisten View-Controller dem Programmierer, der für das Hinzufügen von Unteransichten zur ViewController-Ansicht verantwortlich war, bekannte Einheiten sind ;-) Deshalb denke ich, dass Apple nie die Mühe gemacht, diese Eigenschaft hinzuzufügen.
quelle
Ein bisschen spät, aber hier ist eine Erweiterung, mit der Sie einen Responder jeglichen Typs finden können, einschließlich ViewController.
quelle
Wenn Sie einen Haltepunkt festlegen, können Sie diesen in den Debugger einfügen, um die Ansichtshierarchie zu drucken:
Sie sollten in der Lage sein, die Eltern Ihrer Ansicht irgendwo in diesem Durcheinander zu finden :)
quelle
recursiveDescription
druckt nur die Ansichtshierarchie , nicht die Ansichtscontroller.