Przed iOS 13 prezentowane kontrolery widoku służyły do pokrycia całego ekranu. Po odrzuceniu viewDidAppear
funkcja kontrolera widoku nadrzędnego została wykonana.
Teraz iOS 13 będzie domyślnie przedstawiał kontrolery widoku jako arkusz, co oznacza, że karta częściowo zakryje podstawowy kontroler widoku, co oznacza, że viewDidAppear
nie zostanie wywołany, ponieważ nadrzędny kontroler widoku nigdy tak naprawdę nie zniknął.
Czy istnieje sposób na wykrycie, że przedstawiony arkusz kontrolera widoku został odrzucony ? Jakaś inna funkcja, którą mogę zastąpić w nadrzędnym kontrolerze widoku, zamiast używać jakiegoś delegata ?
Odpowiedzi:
Tak.
Nie. „Jakiś delegat” tak to robisz. Ustaw siebie jako delegata i zastąpienie kontrolera prezentacji
presentationControllerDidDismiss(_:)
.https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/3229889-presentationcontrollerdiddismiss
Brak ogólnego zdarzenia generowanego przez runtime informującego o tym, że prezentowany kontroler widoku, niezależnie od tego, czy został wyświetlony na pełnym ekranie, czy nie, został odrzucony, jest rzeczywiście kłopotliwy; ale to nie jest nowy problem, ponieważ zawsze istniały kontrolery widoku prezentowane na innym ekranie. Tyle, że teraz (w iOS 13) jest ich więcej! Osobne pytanie i odpowiedź poświęcam temu tematowi gdzie indziej: Unified UIViewController "stał się pierwszym" wykrywaniem? .
źródło
Oto przykład kodu nadrzędnego kontrolera widoku, który jest powiadamiany, gdy podrzędny kontroler widoku, który przedstawia jako arkusz (tj. W domyślny sposób iOS 13), jest odrzucany:
public final class Parent: UIViewController, UIAdaptivePresentationControllerDelegate { // This is assuming that the segue is a storyboard segue; // if you're manually presenting, just see the delegate there. public override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "mySegue" { segue.destination.presentationController?.delegate = self; } } public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) { // Only called when the sheet is dismissed by DRAGGING. // You'll need something extra if you call .dismiss() on the child. // (I found that overriding dismiss in the child and calling // presentationController.delegate?.presentationControllerDidDismiss // works well). } }
Odpowiedź Jerland2 jest zdezorientowana, ponieważ (a) pierwotny pytający chciał uzyskać wywołanie funkcji po odrzuceniu arkusza (podczas gdy zaimplementował prezentację PresentationControllerDidAttemptToDismiss, która jest wywoływana, gdy użytkownik próbuje odrzucić arkusz i nie powiedzie się), oraz (b) ustawienie toModalInPresentation jest całkowicie ortogonalny i faktycznie spowoduje, że prezentowany arkusz będzie niemożliwy do odrzucenia (co jest przeciwieństwem tego, czego chce OP).
źródło
Inna opcja powrotu
viewWillAppear
iviewDidAppear
jest ustawionalet vc = UIViewController() vc.modalPresentationStyle = .fullScreen
ta opcja obejmuje pełny ekran i po odrzuceniu wywołuje powyższe metody
źródło
Dla przyszłych czytelników pełna odpowiedź dotycząca implementacji:
// Modal Dismiss iOS 13 modalNavController.presentationController?.delegate = modalVc
// MARK: - iOS 13 Modal (Swipe to Dismiss) extension ModalViewController: UIAdaptivePresentationControllerDelegate { func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) { print("slide to dismiss stopped") self.dismiss(animated: true, completion: nil) } }
self.isModalInPresentation = true
źródło
presentationControllerDidDismiss
powinno działaćpresentationControllerDidAttemptToDismiss
jest przeznaczone dla przypadków, w których użytkownik próbował odrzucić, ale zostało to zablokowane programowo (przeczytaj uważnie dokumentację dla tej metody).presentationControllerWillDismiss
Metoda jest jedna wykryć zamiar użytkownika do odwołania lubpresentationControllerShouldDismiss
do kontrolowania zwalniania lubpresentationControllerDidDismiss
w celu wykrycia faktu bycia odwołanySzybki
Ogólne rozwiązanie do wywołania
viewWillAppear
w iOS13class ViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) print("viewWillAppear") } //Show new viewController @IBAction func show(_ sender: Any) { let newViewController = NewViewController() //set delegate of UIAdaptivePresentationControllerDelegate to self newViewController.presentationController?.delegate = self present(newViewController, animated: true, completion: nil) } } extension UIViewController: UIAdaptivePresentationControllerDelegate { public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) { if #available(iOS 13, *) { //Call viewWillAppear only in iOS 13 viewWillAppear(true) } } }
źródło
dismiss(_)
.FUNC Przeciągnij lub Odrzuć połączenie będzie działać z poniższym kodem.
1) W głównym kontrolerze widoku możesz powiedzieć, który jest jego kontrolerem widoku prezentacji, jak poniżej
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "presenterID" { let navigationController = segue.destination as! UINavigationController if #available(iOS 13.0, *) { let controller = navigationController.topViewController as! presentationviewcontroller // Modal Dismiss iOS 13 controller.presentationController?.delegate = self } else { // Fallback on earlier versions } navigationController.presentationController?.delegate = self } }
2) Ponownie w głównym kontrolerze widoku powiesz, co zrobisz, gdy jego kontroler widoku prezentacji zostanie usunięty
public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) { print("presentationControllerDidDismiss") }
1) W kontrolerze widoku prezentacji, po naciśnięciu przycisku anulowania lub zapisania na tym obrazku. Poniższy kod będzie miał nazwę
self.dismiss(animated: true) { self.presentationController?.delegate?.presentationControllerDidDismiss?(self.presentationController!) }
źródło
Zastąp viewWillDisappear w UIViewController, który jest odrzucany. Zaalarmuje Cię o zwolnieniu za pomocą
isBeingDismissed
flagi boolowskiej.override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isBeingDismissed { print("user is dismissing the vc") } }
** Jeśli użytkownik jest w połowie przesuwania w dół i przesuwa kartę z powrotem w górę, nadal zostanie zarejestrowana jako odrzucona, nawet jeśli karta nie zostanie odrzucona. Ale to skrajny przypadek, na którym możesz nie przejmować się.
źródło
self.dismiss(animated: Bool, completion: (() -> Void)?)
Jeśli chcesz coś zrobić, gdy użytkownik zamyka arkusz modalny z poziomu tego arkusza. Załóżmy, że masz już przycisk Zamknij z
@IBAction
logiką i logiką pokazującą alert przed zamknięciem lub zrób coś innego. Chcesz tylko wykryć moment, w którym użytkownik naciska taki kontroler.Oto jak:
class MyModalSheetViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.presentationController?.delegate = self } @IBAction func closeAction(_ sender: Any) { // your logic to decide to close or not, when to close, etc. } } extension MyModalSheetViewController: UIAdaptivePresentationControllerDelegate { func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool { return false // <-prevents the modal sheet from being closed } func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) { closeAction(self) // <- called after the modal sheet was prevented from being closed and leads to your own logic } }
źródło
Jeśli ktoś nie ma dostępu do prezentowanym kontrolera widoku, można po prostu zastąpić następującą metodę przedstawiając widok kontroler i zmienić
modalPresentationStyle
sięfullScreen
lub można dodać jedną ze strategii wspomniano powyżej, z tym podejściemoverride func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { if let _ = viewControllerToPresent as? TargetVC { viewControllerToPresent.modalPresentationStyle = .fullScreen } super.present(viewControllerToPresent, animated: flag, completion: completion) }
jeśli przedstawiony kontroler widoku jest kontrolerem nawigacji i chcesz sprawdzić kontroler główny, możesz zmienić powyższy warunek na podobny
if let _ = (viewControllerToPresent as? UINavigationController)?.viewControllers.first as? TargetVC { viewControllerToPresent.modalPresentationStyle = .fullScreen }
źródło
Jeśli użyłeś ModalPresentationStyle w FullScreen, zachowanie kontrolera jest normalne.
ConsultarController controllerConsultar = this.Storyboard.InstantiateViewController ("ConsultarController") jako ConsultarController; controllerConsultar.ModalPresentationStyle = UIModalPresentationStyle.FullScreen; this.NavigationController.PushViewController (controllerConsultar, true);
źródło
Z mojego punktu widzenia Apple nie powinno ustawiać
pageSheet
tego domyślniemodalPresentationStyle
Chciałbym przywrócić
fullScreen
domyślny styl za pomocąswizzling
Lubię to:
private func _swizzling(forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) { if let originalMethod = class_getInstanceMethod(forClass, originalSelector), let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) { method_exchangeImplementations(originalMethod, swizzledMethod) } } extension UIViewController { static func preventPageSheetPresentationStyle () { UIViewController.preventPageSheetPresentation } static let preventPageSheetPresentation: Void = { if #available(iOS 13, *) { _swizzling(forClass: UIViewController.self, originalSelector: #selector(present(_: animated: completion:)), swizzledSelector: #selector(_swizzledPresent(_: animated: completion:))) } }() @available(iOS 13.0, *) private func _swizzledPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { if viewControllerToPresent.modalPresentationStyle == .pageSheet || viewControllerToPresent.modalPresentationStyle == .automatic { viewControllerToPresent.modalPresentationStyle = .fullScreen } _swizzledPresent(viewControllerToPresent, animated: flag, completion: completion) } }
A następnie umieść tę linię w swoim
AppDelegate
UIViewController.preventPageSheetPresentationStyle()
źródło
presentingViewController
będą gdzieś błędne, ponieważ nie zostanąviewWillAppear
viewWillAppear
było w pewnym sensie zawsze złe. Oczywiście nie podoba mi się, że Apple przychodzi i wycina mi podłogę. Ale jak mówię, musimy po prostu z tym żyć i robić rzeczy w nowy sposób.presentedController
jest prezentowany kontroler widoku (wywoływany ) i nie wiem, co to jestpresentingViewController
. Na przykład: w niektórych przypadkach muszę użyć,UIViewController.topMostViewController()
który zwraca mi najwyższy kontroler widoku w bieżącym oknie. Dlatego chciałbym zrobić swizzling, aby zachować aktualne zachowanie, aby robić właściwe rzeczy (odświeżanie danych, interfejs użytkownika) wviewWillAppear
moich kontrolerach widoku. Jeśli masz jakieś pomysły na rozwiązanie tego problemu, pomóż.czy nie byłoby łatwo wywołać metodę PresentViewController.viewWillAppear? przed zwolnieniem?
self.presentingViewController?.viewWillAppear(false) self.dismiss(animated: true, completion: nil)
źródło