In iOS 13 können modale Präsentationen, die den Formular- und Seitenblattstil verwenden, mit einer Schwenkbewegung verworfen werden. Dies ist in einem meiner Formularblätter problematisch, da der Benutzer in dieses Feld zieht, was die Geste stört. Es zieht den Bildschirm nach unten, anstatt eine vertikale Linie zu zeichnen.
Wie können Sie das vertikale Wischen deaktivieren, um die Geste in einem als Blatt dargestellten Controller für die modale Ansicht zu schließen?
Durch die Einstellung isModalInPresentation = true
kann das Blatt weiterhin heruntergezogen werden, es wird jedoch nicht geschlossen.
ios
uigesturerecognizer
ios13
Jordan H.
quelle
quelle
Antworten:
Im Allgemeinen sollten Sie nicht versuchen, das Wischen zu deaktivieren, um die Funktionalität zu schließen, da Benutzer erwarten, dass sich alle Formular- / Seitenblätter in allen Apps gleich verhalten. Stattdessen möchten Sie möglicherweise einen Präsentationsstil im Vollbildmodus verwenden. Wenn Sie ein Blatt verwenden möchten, das nicht durch Streichen entfernt werden kann, stellen Sie
isModalInPresentation = true
es ein. Beachten Sie jedoch, dass das Blatt weiterhin vertikal nach unten gezogen werden kann und beim Loslassen der Berührung wieder nach oben springt. Schauen Sie sich die aus UIAdaptivePresentationControllerDelegate Dokumentation zu reagieren , wenn der Benutzer es über Swipe abzutun versucht, neben anderen Maßnahmen.Wenn Sie ein Szenario haben, in dem die Gesten- oder Berührungsbehandlung Ihrer App durch die Funktion zum Streichen zum Streichen beeinflusst wird, habe ich von einem Apple-Techniker einige Ratschläge erhalten, wie dies behoben werden kann.
Wenn Sie verhindern können, dass die Pan-Gestenerkennung des Systems beginnt, wird die gestische Entlassung verhindert. Einige Möglichkeiten, dies zu tun:
Wenn Ihre Leinwandzeichnung mit einer Gestenerkennung wie Ihrer eigenen
UIGestureRecognizer
Unterklasse erstellt wurde, geben Sie diebegan
Phase vor der Entlassungsgeste des Blattes ein. Wenn Sie so schnell wie möglich erkennenUIPanGestureRecognizer
, gewinnen Sie und die Entlassungsgeste des Blattes wird untergraben.Wenn Ihre Leinwandzeichnung mit einem Gestenerkenner erstellt wurde, richten Sie mit
-shouldBeRequiredToFailByGestureRecognizer:
(oder der zugehörigen Delegatenmethode) eine dynamische Fehleranforderung ein , bei der Sie zurückkehren,NO
wenn der übergebene Gestenerkenner a istUIPanGestureRecognizer
.Wenn Ihre Leinwandzeichnung mit manuellem Touch-Handling (z. B.
touchesBegan:
) erstellt wurde, überschreiben Sie-gestureRecognizerShouldBegin
Ihre Touch-Handling-Ansicht und kehren Sie zurück,NO
wenn der übergebene Gestenerkenner a istUIPanGestureRecognizer
.Mit meinem Setup hat sich # 3 als sehr gut erwiesen. Auf diese Weise kann der Benutzer außerhalb der Zeichenfläche nach unten wischen, um sie zu schließen (z. B. in der Navigationsleiste), während der Benutzer zeichnen kann, ohne das Blatt zu bewegen, wie es zu erwarten wäre.
Ich kann nicht empfehlen, nach der Geste zu suchen, um sie zu deaktivieren, da sie ziemlich dynamisch zu sein scheint und sich beispielsweise beim Wechsel zwischen verschiedenen Größenklassen wieder aktivieren kann. Dies könnte sich in zukünftigen Versionen ändern.
quelle
gestureRecognizer(_:,shouldRecognizeSimultaneouslyWith:)
, damit einige Erkenner zusammenarbeiten konnten und andere nicht.UICollectionView
. Verzeihen Sie mir, wenn ich falsch liege, aber müssten wir nicht der Delegierte des Entlassungserkenners werden, um eine dieser Methoden anwenden zu können?Diese Geste befindet sich in der
presentedView
Eigenschaft des Modal View Controllers . Beim Debuggen enthält dasgestureRecognizers
Array dieser Eigenschaft nur ein Element, und das Drucken führte zu etwa dem folgenden Ergebnis:Um diese Geste zu deaktivieren, können Sie wie folgt vorgehen:
let vc = UIViewController() self.present(vc, animated: true, completion: { vc.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = false })
Um es wieder zu aktivieren, setzen Sie es einfach
isEnabled
zurück auftrue
:vc.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = true
Beachten Sie, dass sich iOS 13 noch in der Beta befindet, sodass in einer kommenden Version möglicherweise ein einfacherer Ansatz hinzugefügt wird.
Obwohl diese Lösung im Moment zu funktionieren scheint, würde ich sie nicht empfehlen, da sie in bestimmten Situationen möglicherweise nicht funktioniert oder in zukünftigen iOS-Versionen geändert wird und möglicherweise Auswirkungen auf Ihre App hat.
quelle
viewWillAppear
den angezeigten View Controller eingefügt werden - nützlich für Segues. 🤗UISwipeDismissalGestureRecognizer
. Dies könnte das Problem sein.UITableView
schafft_UISwipeDismissalGestureRecognizer
auch. Wenn Sie einen Navigationscontroller mit einem Root-Ansichtscontroller erstellen, den Stapel modal als Seiten- / Formularblatt darstellen und einen anderen Ansichtscontroller darüber schieben, wird der gesamte Stapel durch die Geste zum Streichen nach unten über a verworfen Der Gestenerkenner wurde irgendwo höher in derUIView
Hierarchie erstellt. Ohne die ausdrückliche Unterstützung von Apple für das Deaktivieren der Verarbeitung von Touch-Ereignissen durch Wischen nach unten, um sie zu schließen, besteht die einzige zuverlässige Lösung (ab Xcode 11 Beta 3) in der VerwendungUIModalPresentationStyle
vonUIModalPresentationFullScreen
.for gesture in guestures where gesture.name == "_UISheetInteractionBackgroundDismissRecognizer" { gesture.isEnabled = false }
Verwenden Sie im vorgestellten ViewController dies in viewDidLoad:
if #available(iOS 13.0, *) { self.isModalInPresentation = true }
quelle
isModalInPresentation = true
kann das Blatt immer noch heruntergezogen werden, es wird jedoch nicht entlassen, was genau das ist, was Sie benötigen, oder es kann je nach Anwendungsfall problematisch sein, wie es für meine Zeichenfläche war.self.modalPresentationStyle = .fullScreen
viewController.isModalInPresentation = true
arbeitete für michIn meinem Fall habe ich einen modalen Bildschirm mit einer Ansicht, die Berührungen zum Erfassen von Kundensignaturen erhält.
Das Deaktivieren der Gestenerkennung im Navigationscontroller löste das Problem und verhinderte, dass die modale interaktive Entlassung überhaupt ausgelöst wurde.
Die folgenden Methoden sind in unserem Modal View Controller implementiert und werden über einen Delegaten aus unserer benutzerdefinierten Signaturansicht aufgerufen.
Angerufen von
touchesBegan
:private func disableDismissalRecognizers() { navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach { $0.isEnabled = false } }
Angerufen von
touchesEnded
:private func enableDismissalRecognizers() { navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach { $0.isEnabled = true } }
Hier ist ein GIF, das das Verhalten zeigt:
Diese als doppelt gekennzeichnete Frage beschreibt das Problem, das ich hatte, besser: Deaktivieren der interaktiven Entlassung des dargestellten Ansichtscontrollers unter iOS 13 beim Ziehen aus der Hauptansicht
quelle
Sie können den Präsentationsstil ändern. Wenn der Vollbildmodus im Vollbildmodus angezeigt wird, ist er deaktiviert
navigationCont.modalPresentationStyle = .fullScreen
quelle
Sie können die UIAdaptivePresentationControllerDelegate-Methode PresentationControllerDidAttemptToDismiss verwenden und den gestureRecognizer in der präsentierten Ansicht deaktivieren. Etwas wie das:
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) { presentationController.presentedView?.gestureRecognizers?.first?.isEnabled = false }
quelle
Für jeden Körper, der Probleme mit Jordans Lösung Nr. 3 hat.
Sie müssen nach dem ROOT-Ansichtscontroller suchen, der angezeigt wird. Abhängig von Ihrem Ansichtsstapel ist dies möglicherweise nicht Ihre aktuelle Ansicht.
Ich musste nach meinen Navigationscontrollern PresentationViewController suchen.
BTW @Jordam: Danke!
UIGestureRecognizer *gesture = [[self.navigationController.presentationController.presentedView gestureRecognizers] firstObject]; if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) { UIPanGestureRecognizer * pan = (UIPanGestureRecognizer *)gesture; pan.delegate = self; }
quelle
Ich benutze das:
-(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; for(UIGestureRecognizer *gr in self.presentationController.presentedView.gestureRecognizers) { if (@available(iOS 11.0, *)) { if([gr.name isEqualToString:@"_UISheetInteractionBackgroundDismissRecognizer"]) { gr.enabled = false; } } }
quelle
Ich werde versuchen, die von @Jordan H bereits vorgeschlagene Methode 2 genauer zu beschreiben:
1) Um die Schwenkgeste des Modalblatts zu erfassen und Entscheidungen darüber zu treffen, fügen Sie diese in die Ansichtssteuerung ein
viewDidLoad
:navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach { $0.delegate = self }
2) Aktivieren Sie die Möglichkeit, die Schwenkgeste zusammen mit Ihren eigenen Gesten zu erfassen
gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)
3) Die eigentliche Entscheidung kann eingehen
gestureRecognizer(_:shouldBeRequiredToFailBy:)
Beispielcode, mit dem die Wischgeste der Schwenkgeste des Blattes vorgezogen wird, wenn beide vorhanden sind. Es wirkt sich nicht auf die ursprüngliche Schwenkgeste in Bereichen aus, in denen keine Wischgestenerkennung vorhanden ist, und daher kann das ursprüngliche "Wischen zum Entlassen" weiterhin wie vorgesehen funktionieren.
extension PeopleViewController: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer === UIPanGestureRecognizer.self && otherGestureRecognizer === UISwipeGestureRecognizer.self { return true } return false } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } }
In meinem Fall habe ich nur wenige Swipe-Gestenerkenner, daher reicht es mir, Typen zu vergleichen. Wenn jedoch mehr vorhanden sind, kann es sinnvoll sein, die gestureRecognizers selbst zu vergleichen (entweder programmgesteuert hinzugefügte oder als Ausgänge vom Interface Builder), wie in beschrieben Dieses Dokument: https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/coordinating_multiple_gesture_recognizers/preferring_one_gesture_over_another
So funktioniert der Code in meinem Fall. Ohne sie wurde die Wischgeste größtenteils ignoriert und funktionierte nur gelegentlich.
quelle
viewDidAppear
(weil meinpresentationController
Null ist, präsentiere ich nur moralisch einen VC). Und ich wiederhole die Überwachung der Ansicht, um herauszufinden, ob die Ansicht eine hatPanGesture
, und setze ihren Delegierten aufself
. Dann kann mein VC nicht nach unten wischen, um zu entlassen. Gibt es eine andere Möglichkeit, mein Problem zu lösen? Bitte helfen Siein IOS 13
if #available(iOS 13.0, *) { obj.isModalInPresentation = true } else { // Fallback on earlier versions }
quelle
In dem Fall, in dem eine
UITableView
oderUICollectionView
die Seitenblatt-Entlassungsgeste initiiert wird, wenn der Benutzer versucht, über das obere Ende der Bildlaufansicht hinaus zu scrollen, kann diese Geste deaktiviert werden, indem eine unsichtbare Geste hinzugefügt wirdUIRefreshControl
, dieendRefreshing
sofort aufruft .Siehe auch https://stackoverflow.com/a/58676756/2419404
quelle
In Vorbereitung (für: Absender :):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == viewControllerSegueID { let controller = segue.destination as! YourViewController controller.modalPresentationStyle = .fullScreen } }
oder, nachdem Sie Ihren Controller initialisiert haben:
let controller = YourViewController() controller.modalPresentationStyle = .fullScreen
quelle
Möglicherweise erhalten Sie zuerst einen Verweis auf den UIPanGestureRecognizer, der die Seitenblattentlassung in der viewDidAppear () -Methode behandelt. Beachten Sie, dass diese Referenz in viewWillAppear () oder viewDidLoad () gleich Null ist. Dann deaktivieren Sie es einfach.
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) presentationController?.presentedView?.gestureRecognizers?.first.isEnabled = false }
Wenn Sie mehr Anpassungen wünschen, anstatt sie vollständig zu deaktivieren, z. B. wenn Sie eine Navigationsleiste im Seitenblatt verwenden, legen Sie den Delegaten dieses UIPanGestureRecognizer auf Ihren eigenen Ansichtscontroller fest. Auf diese Weise können Sie den Gestenerkenner ausschließlich in Ihrer ContentView deaktivieren, während Sie ihn durch Implementierung in Ihrer navBar-Region aktiv halten
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {}
quelle
Für den Navigations-Controller können wir Folgendes verwenden, um eine Swipe-Interaktion für die dargestellte Ansicht zu vermeiden:
if #available(iOS 13.0, *) {navController.isModalInPresentation = true}
quelle