Szenario: Der Benutzer tippt auf eine Schaltfläche eines View Controllers. Der Ansichts-Controller ist (offensichtlich) der oberste im Navigationsstapel. Das Tippen ruft eine Dienstprogrammklassenmethode auf, die für eine andere Klasse aufgerufen wird. Dort passiert etwas Schlimmes und ich möchte genau dort eine Warnung anzeigen, bevor die Steuerung zum View Controller zurückkehrt.
+ (void)myUtilityMethod {
// do stuff
// something bad happened, display an alert.
}
Dies war möglich mit UIAlertView
(aber vielleicht nicht ganz richtig).
Wie präsentieren Sie in diesem Fall eine UIAlertController
, genau dort in myUtilityMethod
?
quelle
Bei WWDC habe ich in einem der Labors Halt gemacht und einem Apple Engineer dieselbe Frage gestellt: "Was war die beste Vorgehensweise für die Anzeige von a
UIAlertController
?" Und er sagte, sie hätten diese Frage oft bekommen und wir scherzten, dass sie eine Sitzung darüber hätten haben sollen. Er sagte, dass Apple intern einUIWindow
mit einem transparenten erstelltUIViewController
und dann das darauf präsentiertUIAlertController
. Grundsätzlich, was in Dylan Bettermans Antwort steht.Ich wollte jedoch keine Unterklasse von verwenden,
UIAlertController
da ich dafür meinen Code in meiner App ändern müsste. Mit Hilfe eines zugeordneten Objekts habe ich eine Kategorie erstelltUIAlertController
, die eineshow
Methode in Objective-C bereitstellt .Hier ist der relevante Code:
Hier ist ein Beispiel für die Verwendung:
Das
UIWindow
, was erstellt wird, wird zerstört, wenn dieUIAlertController
Zuordnung aufgehoben wird, da es das einzige Objekt ist, das das behältUIWindow
. Wenn Sie dasUIAlertController
jedoch einer Eigenschaft zuweisen oder die Anzahl der Aufbewahrungen erhöhen, indem Sie auf die Warnung in einem der Aktionsblöcke zugreifen,UIWindow
bleibt die auf dem Bildschirm und blockiert Ihre Benutzeroberfläche. Lesen Sie den obigen Verwendungsbeispielcode, um zu vermeiden, dass Sie darauf zugreifen müssenUITextField
.Ich habe ein GitHub-Repo mit einem Testprojekt gemacht: FFGlobalAlertController
quelle
viewDidDisappear:
in einer Kategorie sieht nach einer schlechten Idee aus. Im Wesentlichen konkurrieren Sie mit der Implementierung des Frameworks vonviewDidDisappear:
. Im Moment mag es in Ordnung sein, aber wenn Apple beschließt, diese Methode in Zukunft zu implementieren, können Sie sie nicht aufrufen (dh es gibt keine Analogie dazusuper
, die auf die primäre Implementierung einer Methode aus einer Kategorieimplementierung hinweist). .prefersStatusBarHidden
undpreferredStatusBarStyle
ohne zusätzliche Unterklasse?Schnell
Ziel c
quelle
Mit Swift 2.2 können Sie Folgendes tun:
Und Swift 3.0:
quelle
Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!
.visibleViewController
Eigenschaft jederzeit abrufen, um zu sehen, von welchem Controller die Warnung angezeigt werden soll . Schauen Sie sich die Dokumente anZiemlich generisch
UIAlertController
extension
für alle Fälle vonUINavigationController
und / oderUITabBarController
. Funktioniert auch, wenn gerade eine modale VC auf dem Bildschirm angezeigt wird.Verwendung:
Dies ist die Erweiterung:
quelle
UI
Klasse, die einen (schwachen!)currentVC
Typ enthält.UIViewController
Ich habe eine,BaseViewController
die von erbtUIViewController
undUI.currentVC
aufself
on undviewDidAppear
dann aufnil
on setztviewWillDisappear
. Alle meine View Controller in der App erbenBaseViewController
. Auf diese Weise, wenn Sie etwas in habenUI.currentVC
(es ist nichtnil
...) - es ist definitiv nicht in der Mitte einer Präsentationsanimation, und Sie können es bitten, Ihre zu präsentierenUIAlertController
.else { if let presentedViewController = controller.presentedViewController { presentedViewController.presentViewController(self, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } }
Um die Antwort von agilityvision zu verbessern , müssen Sie ein Fenster mit einem transparenten Root-Ansichts-Controller erstellen und von dort aus die Warnansicht anzeigen.
Aber solange Sie eine Aktion in Ihren Alarm - Controller haben, brauchen Sie nicht einen Verweis auf das Fenster zu halten . Als letzten Schritt des Aktionshandlerblocks müssen Sie nur das Fenster als Teil der Bereinigungsaufgabe ausblenden. Durch einen Verweis auf das Fenster im Handlerblock wird ein temporärer Zirkelverweis erstellt, der unterbrochen wird, sobald der Alarmcontroller geschlossen wird.
quelle
Die folgende Lösung funktionierte nicht , obwohl sie bei allen Versionen vielversprechend aussah. Diese Lösung generiert WARNUNG .
Warnung: Versuchen Sie zu präsentieren, auf dessen Ansicht sich nicht die Fensterhierarchie befindet!
https://stackoverflow.com/a/34487871/2369867 => Das sieht dann vielversprechend aus. Aber es war nicht in
Swift 3
. Ich beantworte dies in Swift 3 und dies ist kein Vorlagenbeispiel.Dies ist ein ziemlich voll funktionsfähiger Code für sich, sobald Sie ihn in eine Funktion einfügen.
Dies ist getesteter und funktionierender Code in Swift 3.
quelle
UIWindow
Fenster speichern, sonst wird das Fenster freigegeben und verschwindet kurz nach dem Verlassen des Gültigkeitsbereichs.Hier ist die Antwort von mythicalcoder als Erweiterung, getestet und funktioniert in Swift 4:
Anwendungsbeispiel:
quelle
Dies funktioniert in Swift für normale Ansichts-Controller und selbst wenn ein Navigations-Controller auf dem Bildschirm angezeigt wird:
quelle
UIWindow
ablasse , reagiert der nicht. Etwas mit demwindowLevel
wahrscheinlich zu tun . Wie kann ich darauf reagieren?alertWindow
auf,nil
wenn Sie damit fertig sind.Wenn Sie Zevs Antwort hinzufügen (und wieder zu Objective-C wechseln), könnten Sie in eine Situation geraten, in der Ihr Root-View-Controller eine andere VC über einen Abschnitt oder etwas anderes präsentiert. Wenn Sie den PresentViewController auf dem Root-VC aufrufen, wird Folgendes erledigt:
Dies hat ein Problem behoben, bei dem der Root-VC zu einem anderen VC übergegangen war, und anstatt den Alert-Controller anzuzeigen, wurde eine Warnung wie die oben angegebene ausgegeben:
Ich habe es nicht getestet, aber dies kann auch erforderlich sein, wenn Ihr Root-VC zufällig ein Navigationscontroller ist.
quelle
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x15cd4afe0>)
rootViewController.presentedViewController
wenn es nicht Null ist, sonst verwendenrootViewController
. Für eine vollständig generische Lösung kann es erforderlich sein, die Kette vonpresentedViewController
s zutopmost
Die Antwort von @ agilityvision wurde in Swift4 / iOS11 übersetzt. Ich habe keine lokalisierten Zeichenfolgen verwendet, aber Sie können dies leicht ändern:
quelle
window.backgroundColor = UIColor.clear
das behoben.viewController.view.backgroundColor = UIColor.clear
scheint nicht notwendig zu sein.UIAlertController
The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
Erstellen Sie eine Erweiterung wie in der Antwort von Aviel Gross. Hier haben Sie die Objective-C-Erweiterung.
Hier haben Sie die Header-Datei * .h
Und Umsetzung: * .m
Sie verwenden diese Erweiterung in Ihrer Implementierungsdatei wie folgt:
quelle
Kreuz poste meine Antwort da diese beiden Threads nicht als Dupes gekennzeichnet sind ...
Nun, da dies
UIViewController
Teil der Responderkette ist, können Sie Folgendes tun:quelle
Die Antwort von Zev Eisenberg ist einfach und unkompliziert, funktioniert jedoch nicht immer und kann mit dieser Warnmeldung fehlschlagen:
Dies liegt daran, dass sich der Windows-RootViewController nicht oben in den dargestellten Ansichten befindet. Um dies zu korrigieren, müssen wir die Präsentationskette durchlaufen, wie in meinem in Swift 3 geschriebenen UIAlertController-Erweiterungscode gezeigt:
Updates am 15.09.2017:
Getestet und bestätigt, dass die obige Logik im neu verfügbaren GM-Seed für iOS 11 immer noch hervorragend funktioniert. Die von Agilityvision am besten gewählte Methode funktioniert jedoch nicht: Die Alarmansicht wird in einer neu geprägten dargestellt
UIWindow
befindet sich unter der Tastatur und verhindert möglicherweise, dass der Benutzer auf seine Schaltflächen tippt. Dies liegt daran, dass in iOS 11 alle Fensterebenen, die höher als die des Tastaturfensters sind, auf eine darunter liegende Ebene abgesenkt werden.Ein Artefakt der Präsentation von
keyWindow
obwohl ist die Animation der Tastatur, die nach unten rutscht, wenn eine Warnung angezeigt wird, und wieder nach oben, wenn die Warnung geschlossen wird. Wenn Sie möchten, dass die Tastatur während der Präsentation dort bleibt, können Sie versuchen, sie im oberen Fenster selbst zu präsentieren, wie im folgenden Code gezeigt:Der einzige nicht so große Teil des obigen Codes ist, dass er den Klassennamen überprüft
UIRemoteKeyboardWindow
, um sicherzustellen, dass wir ihn auch einschließen können. Trotzdem funktioniert der obige Code hervorragend in iOS 9, 10 und 11 GM-Seeds, mit der richtigen Farbtonfarbe und ohne die Artefakte der Tastatur.quelle
Swift 4+
Lösung Ich benutze seit Jahren ohne Probleme. Zunächst
UIWindow
möchte ich den sichtbaren ViewController finden. HINWEIS : Wenn Sie benutzerdefinierte Collection * -Klassen (z. B. das Seitenmenü) verwenden, sollten Sie den Handler für diesen Fall in der folgenden Erweiterung hinzufügen. Obersten View - Controller Nachdem ich es einfach zu präsentierenUIAlertController
wieUIAlertView
.quelle
Aufbauend auf den Antworten von mythicalcoder und bobbyrehm für iOS 13 :
Wenn Sie in iOS 13 ein eigenes Fenster erstellen, in dem die Warnung angezeigt wird, müssen Sie einen starken Verweis auf dieses Fenster haben. Andernfalls wird Ihre Warnung nicht angezeigt, da das Fenster sofort freigegeben wird, wenn seine Referenz den Gültigkeitsbereich verlässt.
Darüber hinaus müssen Sie den Verweis nach dem Schließen der Warnung erneut auf Null setzen, um das Fenster zu entfernen und weiterhin Benutzerinteraktionen im Hauptfenster darunter zuzulassen.
Sie können eine
UIViewController
Unterklasse erstellen , um die Fensterspeicherverwaltungslogik zu kapseln:Sie können dies so verwenden, wie es ist, oder wenn Sie eine bequeme Methode für Ihre möchten
UIAlertController
, können Sie sie in eine Erweiterung einfügen:quelle
dismiss
den WindowAlertPresentationController direkt aufalert.presentingViewController?.dismiss(animated: true, completion: nil)
Kurzform zur Darstellung der Warnung in Ziel-C:
Wo
alertController
ist deinUIAlertController
Objekt?HINWEIS: Sie müssen auch sicherstellen, dass Ihre Hilfsklasse erweitert wird
UIViewController
quelle
Wenn jemand interessiert ist, habe ich eine Swift 3-Version von @agilityvision answer erstellt. Der Code:
quelle
Mit diesem können Sie Ihre Warnung einfach so präsentieren
Zu beachten ist, dass, wenn derzeit ein UIAlertController angezeigt wird, a zurückgegeben
UIApplication.topMostViewController
wirdUIAlertController
. Präsentieren auf einemUIAlertController
hat seltsames Verhalten und sollte vermieden werden. Daher sollten Sie dies entweder manuell überprüfen,!(UIApplication.topMostViewController is UIAlertController)
bevor Sie es präsentieren, oder einenelse if
Fall hinzufügen , um nil if zurückzugebenself is UIAlertController
quelle
Sie können die aktuelle Ansicht oder Steuerung als Parameter senden:
quelle
Kevin Sliech bot eine großartige Lösung.
Ich verwende jetzt den folgenden Code in meiner Hauptunterklasse von UIViewController.
Eine kleine Änderung, die ich vorgenommen habe, war zu überprüfen, ob der beste Präsentationscontroller kein einfacher UIViewController ist. Wenn nicht, muss es eine VC sein, die eine einfache VC präsentiert. Daher geben wir die VC zurück, die stattdessen präsentiert wird.
Scheint bei meinen Tests bisher alles geklappt zu haben.
Danke Kevin!
quelle
Neben tollen Antworten ( Agilityvision , Adib , Malhal ). Verwenden Sie diesen Block, um die Verfügbarkeit von Fenstern zu beobachten, um ein Warteschlangenverhalten wie in guten alten UIAlertViews zu erreichen (Überlappung von Warnfenstern vermeiden):
Vollständiges Beispiel:
Auf diese Weise können Sie Überlappungen von Warnfenstern vermeiden. Dieselbe Methode kann verwendet werden, um Controller für die Warteschlangenansicht für eine beliebige Anzahl von Fensterebenen zu trennen und einzufügen.
quelle
Ich habe alles versucht, aber ohne Erfolg. Die Methode, die ich für Swift 3.0 verwendet habe:
quelle
Einige dieser Antworten haben nur teilweise für mich funktioniert. Die Kombination in der folgenden Klassenmethode in AppDelegate war die Lösung für mich. Es funktioniert auf dem iPad, in UITabBarController-Ansichten, in UINavigationController und bei der Präsentation von Modalen. Getestet auf iOS 10 und 13.
Verwendung:
quelle
Unterstützung für iOS13-Szenen (bei Verwendung von UIWindowScene)
quelle
Sie können versuchen, eine Kategorie
UIViewController
mit mehtod wie- (void)presentErrorMessage;
And zu implementieren, und innerhalb dieser Methode implementieren Sie UIAlertController und präsentieren sie dann aufself
. Dann haben Sie in Ihrem Kundencode so etwas wie:[myViewController presentErrorMessage];
Auf diese Weise vermeiden Sie unnötige Parameter und Warnungen, dass sich die Ansicht nicht in der Fensterhierarchie befindet.
quelle
myViewController
im Code habe, wo das Schlimme passiert. Dies ist eine Dienstprogrammmethode, die nichts über den View Controller weiß, der sie aufgerufen hat.UIAlertView
führte dazu, dass ich diese Regel an einigen Stellen brach.Es gibt 2 Ansätze, die Sie verwenden können:
-Verwenden
UIAlertView
Sie stattdessen 'UIActionSheet' (nicht empfohlen, da es in iOS 8 veraltet ist, aber jetzt funktioniert).- Erinnern Sie sich irgendwie an den zuletzt angezeigten View Controller. Hier ist ein Beispiel.
Verwendung:
quelle
Ich verwende diesen Code mit einigen kleinen persönlichen Variationen in meiner AppDelegate-Klasse
quelle
Scheint zu funktionieren:
quelle
Erstellen Sie die Hilfsklasse AlertWindow und verwenden Sie sie als
quelle