Angenommen, ich habe eine Instanz einer View-Controller-Klasse namens VC2. In VC2 gibt es eine Schaltfläche "Abbrechen", die sich selbst schließt. Ich kann jedoch keinen Rückruf erkennen oder empfangen, wenn die Schaltfläche "Abbrechen" ausgelöst wurde. VC2 ist eine Black Box.
Ein Ansichts-Controller (VC1 genannt) präsentiert VC2 mithilfe der presentViewController:animated:completion:
Methode.
Welche Optionen muss VC1 erkennen, wenn VC2 entlassen wurde?
Bearbeiten: Aus dem Kommentar von @rory mckinnel und der Antwort von @NicolasMiari habe ich Folgendes versucht:
In VC2:
-(void)cancelButton:(id)sender
{
[self dismissViewControllerAnimated:YES completion:^{
}];
// [super dismissViewControllerAnimated:YES completion:^{
//
// }];
}
In VC1:
//-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
- (void)dismissViewControllerAnimated:(BOOL)flag
completion:(void (^ _Nullable)(void))completion
{
NSLog(@"%s ", __PRETTY_FUNCTION__);
[super dismissViewControllerAnimated:flag completion:completion];
// [self dismissViewControllerAnimated:YES completion:^{
//
// }];
}
Aber der dismissViewControllerAnimated
im VC1 wurde nicht angerufen.
ios
uiviewcontroller
user523234
quelle
quelle
dismissViewControllerAnimated
Ihren VC1-Controller überschreiben, wird er meiner Meinung nach aufgerufen, wenn Sie auf VC2 auf Abbrechen klicken. Erkennen Sie die Entlassung und rufen Sie dann die Superklassenversion auf, die die eigentliche Entlassung ausführt.[self.presentingViewController dismissViewControllerAnimated]
. Es kann sein, dass der innere Code einen anderen Mechanismus hat, um den Präsentator zu bitten, die Entlassung vorzunehmen.Antworten:
Gemäß den Dokumenten ist der anwesende Controller für die tatsächliche Entlassung verantwortlich. Wenn sich der vorgestellte Controller selbst entlässt, fordert er den Präsentator auf, dies zu tun. Wenn Sie also descViewControllerAnimated in Ihrem VC1-Controller überschreiben, wird es meiner Meinung nach aufgerufen, wenn Sie auf VC2 auf Abbrechen klicken. Erkennen Sie die Entlassung und rufen Sie dann die Superklassenversion auf, die die eigentliche Entlassung ausführt.
Wie aus der Diskussion hervorgeht, scheint dies nicht zu funktionieren. Anstatt sich auf den zugrunde liegenden Mechanismus zu verlassen, anstatt
dismissViewControllerAnimated:completion
VC2 selbst aufzurufen, rufen SiedismissViewControllerAnimated:completion
aufself.presentingViewController
in VC2. Dadurch wird Ihre Überschreibung direkt aufgerufen.Insgesamt wäre es besser, wenn VC2 einen Block bereitstellt, der aufgerufen wird, wenn der Modal Controller fertig ist.
Geben Sie in VC2 eine Blockeigenschaft mit dem Namen an
onDoneBlock
.In VC1 präsentieren Sie Folgendes:
Erstellen Sie in VC1 VC2
Stellen Sie den Fertig-Handler für VC2 wie folgt ein:
VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};
Präsentieren Sie den VC2-Controller wie gewohnt mit [self presentViewController: VC2 animiert: JA Vervollständigung: Null];
In VC2 im Aufruf "Zielaktion abbrechen"
self.onDoneBlock();
Das Ergebnis ist, dass VC2 jedem, der es auslöst, mitteilt, dass es fertig ist. Sie können die
onDoneBlock
Argumente erweitern, um anzugeben, ob das Modal komletiert, abgebrochen, erfolgreich usw. ist.quelle
Es gibt eine spezielle Boolesche Eigenschaft innerhalb
UIViewController
genannt ,isBeingDismissed
dass Sie für diesen Zweck verwendet werden können:override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isBeingDismissed { // TODO: Do your stuff here. } }
quelle
viewDidAppear
.Verwenden Sie eine Block-Eigenschaft
In VC2 deklarieren
var onDoneBlock : ((Bool) -> Void)?
Setup in VC1
VC2.onDoneBlock = { result in // Do something }
Rufen Sie VC2 an, wenn Sie entlassen werden
onDoneBlock!(true)
quelle
Sowohl der präsentierende als auch der präsentierte View Controller können aufrufen
dismissViewController:animated:
, um den präsentierten Ansichtscontroller zu schließen.Die erstere Option ist (wohl) die "richtige", was das Design betrifft: Derselbe "übergeordnete" Ansichts-Controller ist für das Präsentieren und Löschen des modalen ("untergeordneten") Ansichts-Controllers verantwortlich.
Letzteres ist jedoch praktischer: In der Regel ist die Schaltfläche "Entlassen" an die Ansicht des dargestellten Ansichtscontrollers angehängt, und der Ansichtscontroller wurde als Aktionsziel festgelegt.
Wenn Sie den früheren Ansatz verwenden, kennen Sie bereits die Codezeile in Ihrem Presenting View Controller, in der die Entlassung erfolgt: Führen Sie Ihren Code entweder unmittelbar danach aus
dismissViewControllerAnimated:completion:
oder innerhalb des Abschlussblocks aus.Wenn Sie den letzteren Ansatz verwenden (der Controller für präsentierte Ansichten schließt sich selbst ab), denken Sie daran, dass der Aufruf
dismissViewControllerAnimated:completion:
vom Controller für präsentierte Ansichten UIKit veranlasst, diese Methode wiederum auf dem Controller für präsentierte Ansichten aufzurufen:( Quelle: UIViewController-Klassenreferenz )
Um ein solches Ereignis abzufangen, können Sie diese Methode im darstellenden Ansichts-Controller überschreiben :
override func dismiss(animated flag: Bool, completion: (() -> Void)?) { super.dismiss(animated: flag, completion: completion) // Your custom code here... }
quelle
extension Foo: UIAdaptivePresentationControllerDelegate { func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { //call whatever you want } } vc.presentationController?.delegate = foo
quelle
iOS 13.0+
nurSie können die Abwicklungssegmentierung verwenden, um diese Aufgabe auszuführen, ohne den EntlassungModalViewController verwenden zu müssen. Definieren Sie in Ihrem VC1 eine Abwicklungsmethode.
Unter diesem Link erfahren Sie, wie Sie den Abwicklungsbereich erstellen: https://stackoverflow.com/a/15839298/5647055 .
Angenommen, Ihr Abwicklungsbereich ist eingerichtet. In der für Ihre Schaltfläche "Abbrechen" definierten Aktionsmethode können Sie den Übergang wie folgt ausführen:
[self performSegueWithIdentifier:@"YourUnwindSegueName" sender:nil];
Wenn Sie jetzt die Taste "Abbrechen" im VC2 drücken, wird sie geschlossen und VC1 wird angezeigt. Es wird auch die in VC1 definierte Abwicklungsmethode aufgerufen. Jetzt wissen Sie, wann der dargestellte Ansichts-Controller geschlossen wird.
quelle
Ich benutze das Folgende, um einem Koordinator zu signalisieren, dass der View Controller "fertig" ist. Dies wird in einer
AVPlayerViewController
Unterklasse in einer tvOS-Anwendung verwendet und wird aufgerufen, nachdem der Übergang zur Entlassung von playerVC abgeschlossen ist:class PlayerViewController: AVPlayerViewController { var onDismissal: (() -> Void)? override func beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) { super.beginAppearanceTransition(isAppearing, animated: animated) transitionCoordinator?.animate(alongsideTransition: nil, completion: { [weak self] _ in if !isAppearing { self?.onDismissal?() } }) } }
quelle
Unter Verwendung der
willMove(toParent: UIViewController?)
in der folgenden Art und Weise schien für mich an der Arbeit. (Getestet auf iOS12).override func willMove(toParent parent: UIViewController?) { super.willMove(toParent: parent); if parent == nil { // View controller is being removed. // Perform onDismiss action } }
quelle
@ user523234 - "Der entlassene ViewViewControllerAnimated im VC1 wurde jedoch nicht aufgerufen."
Sie können nicht davon ausgehen, dass VC1 tatsächlich die Präsentation durchführt - es könnte sich beispielsweise um den Root-View-Controller VC0 handeln. Es sind 3 View Controller beteiligt:
In Ihrem Beispiel
VC1 = sourceViewController
,VC2 = presentedViewController
,?? = presentingViewController
- vielleicht VC1, vielleicht auch nicht.Sie können sich jedoch immer darauf verlassen, dass VC1.animationControllerForDismissedController aufgerufen wird (wenn Sie die Delegatmethoden implementiert haben), wenn Sie VC2 schließen, und in dieser Methode können Sie mit VC1 tun, was Sie wollen
quelle
Ich habe diesen Beitrag so oft gesehen, als ich mich mit diesem Problem befasste, dass ich dachte, ich könnte endlich etwas Licht auf eine mögliche Antwort werfen.
Wenn Sie wissen möchten , ob vom Benutzer initiierte Aktionen (wie Gesten auf dem Bildschirm) die Entlassung eines UIActionControllers ausgelöst haben und keine Zeit in die Erstellung von Unterklassen oder Erweiterungen oder was auch immer in Ihrem Code investieren möchten, gibt es eine Alternative.
Wie sich herausstellt, verfügt die Eigenschaft popoverPresentationController eines UIActionControllers (oder vielmehr eines beliebigen UIViewControllers) über einen Delegaten, den Sie jederzeit in Ihrem Code festlegen können , der vom Typ UIPopoverPresentationControllerDelegate ist und über die folgenden Methoden verfügt:
Weisen Sie den Delegaten von Ihrem Aktionscontroller zu, implementieren Sie die Methode (n) Ihrer Wahl in der Delegatenklasse (Ansicht, Ansichtscontroller oder was auch immer) und voila!
Hoffe das hilft.
quelle
Wählen Sie Unterklasse von: UIStoryboardSegue
Gehen Sie zur Datei DismissSegue.m und notieren Sie den folgenden Code:
- (void)perform { UIViewController *sourceViewController = self.sourceViewController; [sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; }
Öffnen Sie das Storyboard und ziehen Sie dann bei gedrückter Strg-Taste von der Schaltfläche "Abbrechen" auf "VC1". Wählen Sie "Aktionssegment" als "Schließen" und Sie sind fertig.
quelle
Wenn Sie überschreiben, dass der Ansichts-Controller verkleinert wird:
override func removeFromParentViewController() { super.removeFromParentViewController() // your code here }
Zumindest hat das bei mir funktioniert.
quelle
Sie können den geschlossenen uiviewcontroller mit Unwind Segues behandeln.
https://developer.apple.com/library/content/technotes/tn2298/_index.html
https://spin.atomicobject.com/2014/12/01/program-ios-unwind-segue/
quelle
override
ingviewDidAppear
hat den Trick für mich getan. Ich habe einen Singleton in meinem Modal verwendet und bin nun in der Lage, diesen innerhalb des aufrufenden VC, des Modals und überall sonst einzustellen und zu entfernen.quelle
viewDidAppear
?Override-
viewWillDisappear
Funktion im dargestellten View Controller.override func viewWillDisappear(_ animated: Bool) { //Your code here }
quelle
viewDidAppear
.Wie bereits erwähnt, ist die Lösung zu verwenden
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)
.Wenn Sie sich fragen, warum
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)
dies nicht immer zu funktionieren scheint, stellen Sie möglicherweise fest, dass der Anruf von einem abgefangen wird,UINavigationController
wenn er verwaltet wird. Ich habe eine Unterklasse geschrieben, die helfen soll:class DismissingNavigationController: UINavigationController { override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { super.dismiss(animated: flag, completion: completion) topViewController?.dismiss(animated: flag, completion: completion) } }
quelle
Wenn Sie das Löschen von View Controllern behandeln möchten, sollten Sie den folgenden Code verwenden.
- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.isBeingDismissed && self.completion != NULL) { self.completion(); } }
Leider können wir die Vervollständigung in der überschriebenen Methode nicht aufrufen,
(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^ _Nullable)(void))completion;
da diese Methode nur aufgerufen wird, wenn Sie die Entlassungsmethode dieses Ansichtscontrollers aufrufen.quelle
viewWillDisappear
auch nicht richtig ohne Pairing mitviewDidAppear
.Eine andere Möglichkeit besteht darin, EntlassungTransitionDidEnd () Ihres benutzerdefinierten UIPresentationControllers abzuhören
quelle
Ich habe deinit für den ViewController verwendet
deinit { dataSource.stopUpdates() }
quelle