Wie kann ich ein erzwingen, UIScrollView
bei dem Paging und Scrollen aktiviert sind, um sich zu einem bestimmten Zeitpunkt nur vertikal oder horizontal zu bewegen?
Ich verstehe, dass die directionalLockEnabled
Eigenschaft dies erreichen sollte, aber ein diagonales Wischen bewirkt immer noch, dass die Ansicht diagonal scrollt, anstatt die Bewegung auf eine einzelne Achse zu beschränken.
Bearbeiten: Um klarer zu sein, möchte ich dem Benutzer erlauben, horizontal ODER vertikal zu scrollen, aber nicht beide gleichzeitig.
Antworten:
Du bist in einer sehr schwierigen Situation, muss ich sagen.
Beachten Sie, dass Sie eine UIScrollView mit verwenden müssen
pagingEnabled=YES
, um zwischen Seiten zu wechseln, aberpagingEnabled=NO
vertikal scrollen müssen.Es gibt 2 mögliche Strategien. Ich weiß nicht, welches funktionieren wird / einfacher zu implementieren ist, also versuchen Sie beide.
Erstens: verschachtelte UIScrollViews. Ehrlich gesagt, ich muss noch eine Person sehen, die das zum Laufen gebracht hat. Ich habe mich jedoch persönlich nicht genug bemüht, und meine Praxis zeigt, dass Sie UIScrollView dazu bringen können , alles zu tun, was Sie wollen , wenn Sie sich genug anstrengen .
Die Strategie besteht also darin, die äußere Bildlaufansicht nur für das horizontale Bildlauf und die innere Bildlaufansicht nur für das vertikale Bildlauf zu verwenden. Um dies zu erreichen, müssen Sie wissen, wie UIScrollView intern funktioniert. Es überschreibt die
hitTest
Methode und gibt sich immer selbst zurück, sodass alle Berührungsereignisse in UIScrollView gespeichert werden. Dann innentouchesBegan
,touchesMoved
etc es überprüft , ob es im Falle interessiert, und entweder Griffe oder leitet sie an den inneren Komponenten.Um zu entscheiden, ob die Berührung behandelt oder weitergeleitet werden soll, startet UIScrollView einen Timer, wenn Sie sie zum ersten Mal berühren:
Wenn Sie Ihren Finger innerhalb von 150 ms nicht wesentlich bewegt haben, wird das Ereignis an die Innenansicht weitergeleitet.
Wenn Sie Ihren Finger innerhalb von 150 ms erheblich bewegt haben, beginnt er mit dem Scrollen (und leitet das Ereignis niemals an die innere Ansicht weiter).
Beachten Sie, dass beim Berühren einer Tabelle (die eine Unterklasse der Bildlaufansicht ist) und beim sofortigen Bildlauf die von Ihnen berührte Zeile niemals hervorgehoben wird.
Wenn Sie nicht Ihren Finger bewegt deutlich innerhalb 150ms und UIScrollView begann an der Innenansicht die Ereignisse vorbei, aber dann haben Sie den Finger weit genug für das Scrollen bewegt zu beginnen, UIScrollView ruft
touchesCancelled
auf der Innenansicht und Scrollen beginnt.Beachten Sie, dass beim Berühren einer Tabelle, halten Sie den Finger etwas gedrückt und beginnen Sie mit dem Scrollen. Die Zeile, die Sie berührt haben, wird zuerst hervorgehoben, anschließend jedoch deaktiviert.
Diese Ereignisfolge kann durch Konfiguration von UIScrollView geändert werden:
delaysContentTouches
NEIN ist, wird kein Timer verwendet - die Ereignisse gehen sofort an die innere Steuerung (werden jedoch abgebrochen, wenn Sie Ihren Finger weit genug bewegen).cancelsTouches
NEIN ist, wird nach dem Senden der Ereignisse an ein Steuerelement kein Bildlauf mehr ausgeführt.Beachten Sie, dass es UIScrollView ist , das erhält alle
touchesBegin
,touchesMoved
,touchesEnded
undtouchesCanceled
Ereignisse von Cocoatouch (weil seinhitTest
es so zu tun , erzählt). Es leitet sie dann an die innere Ansicht weiter, wenn es will, solange es will.Nachdem Sie alles über UIScrollView wissen, können Sie dessen Verhalten ändern. Ich wette, Sie möchten dem vertikalen Scrollen den Vorzug geben, sodass die Ansicht in vertikaler Richtung rollt, sobald der Benutzer die Ansicht berührt und seinen Finger bewegt (auch nur geringfügig). Wenn der Benutzer seinen Finger jedoch weit genug in horizontaler Richtung bewegt, möchten Sie das vertikale Scrollen abbrechen und das horizontale Scrollen starten.
Sie möchten Ihre äußere UIScrollView in Unterklassen unterteilen (z. B. nennen Sie Ihre Klasse RemorsefulScrollView), damit anstelle des Standardverhaltens alle Ereignisse sofort an die innere Ansicht weitergeleitet werden und nur dann gescrollt wird, wenn eine signifikante horizontale Bewegung erkannt wird.
Wie kann RemorsefulScrollView so verhalten?
Es sieht so aus, als ob das Deaktivieren des vertikalen Bildlaufs und das Setzen
delaysContentTouches
auf NO dazu führen sollte, dass verschachtelte UIScrollViews funktionieren. Leider nicht; UIScrollView scheint einige zusätzliche Filter für schnelle Bewegungen durchzuführen (die nicht deaktiviert werden können), so dass UIScrollView, selbst wenn es nur horizontal gescrollt werden kann, immer schnell genug vertikale Bewegungen aufnimmt (und ignoriert).Der Effekt ist so stark, dass vertikales Scrollen in einer verschachtelten Bildlaufansicht unbrauchbar wird. (Es scheint, dass Sie genau dieses Setup haben, also versuchen Sie es: Halten Sie einen Finger 150 ms lang gedrückt und bewegen Sie ihn dann in vertikaler Richtung - verschachteltes UIScrollView funktioniert dann wie erwartet!)
Dies bedeutet, dass Sie den UIScrollView-Code nicht für die Ereignisbehandlung verwenden können. Sie müssen alle vier Berührungsbehandlungsmethoden in RemorsefulScrollView überschreiben und zuerst Ihre eigene Verarbeitung durchführen. Das Ereignis wird nur an
super
(UIScrollView) weitergeleitet, wenn Sie sich für das horizontale Scrollen entschieden haben.Sie müssen jedoch
touchesBegan
an UIScrollView übergeben, da es eine Basiskoordinate für das zukünftige horizontale Scrollen speichern soll (wenn Sie später entscheiden, dass es sich um ein horizontales Scrollen handelt). Sie können später nichttouchesBegan
an UIScrollView senden , da Sie dastouches
Argument nicht speichern können : Es enthält Objekte, die vor dem nächstentouchesMoved
Ereignis mutiert werden , und Sie können den alten Status nicht reproduzieren.Sie müssen
touchesBegan
also sofort an UIScrollView übergeben, aber Sie werden alle weiterentouchesMoved
Ereignisse ausblenden , bis Sie sich entscheiden, horizontal zu scrollen. NeintouchesMoved
bedeutet kein Scrollen, dahertouchesBegan
schadet diese Initiale nicht. Stellen SiedelaysContentTouches
jedoch NO ein, damit keine zusätzlichen Überraschungstimer stören.(Offtopic - Im Gegensatz zu Ihnen kann UIScrollView Berührungen ordnungsgemäß speichern und das ursprüngliche Ereignis später reproduzieren und weiterleiten . Es hat den unfairen Vorteil, unveröffentlichte APIs zu verwenden, sodass Berührungsobjekte geklont werden können ,
touchesBegan
bevor sie mutiert werden.)Da Sie immer weiterleiten
touchesBegan
, müssen Sie auch weiterleitentouchesCancelled
undtouchesEnded
. Sie haben zu drehen ,touchesEnded
intouchesCancelled
, aber, weil UIScrollView würde interpretierentouchesBegan
,touchesEnded
Sequenz als Touch-Klick, und es an die Innenansicht weiterleiten. Sie leiten die richtigen Ereignisse bereits selbst weiter, sodass UIScrollView niemals etwas weiterleiten soll.Grundsätzlich ist hier Pseudocode für das, was Sie tun müssen. Der Einfachheit halber erlaube ich niemals horizontales Scrollen, nachdem ein Multitouch-Ereignis aufgetreten ist.
Ich habe nicht versucht, dies auszuführen oder sogar zu kompilieren (und die gesamte Klasse in einen Nur-Text-Editor eingegeben), aber Sie können mit dem oben Gesagten beginnen und es hoffentlich zum Laufen bringen.
Der einzige versteckte Haken, den ich sehe, ist, dass, wenn Sie RemorsefulScrollView untergeordnete Ansichten hinzufügen, die nicht zu UIScrollView gehören, die Berührungsereignisse, die Sie an ein untergeordnetes Element weiterleiten, möglicherweise über die Antwortkette zu Ihnen zurückkehren, wenn das untergeordnete Element nicht immer wie UIScrollView mit Berührungen umgeht. Eine kugelsichere RemorsefulScrollView-Implementierung würde vor
touchesXxx
Wiedereintritt schützen .Zweite Strategie: Wenn verschachtelte UIScrollViews aus irgendeinem Grund nicht funktionieren oder sich als zu schwierig erweisen, um sie richtig zu machen, können Sie versuchen, mit nur einer UIScrollView auszukommen und ihre
pagingEnabled
Eigenschaft im Handumdrehen von IhrerscrollViewDidScroll
Delegatenmethode zu ändern .Um ein diagonales Scrollen zu verhindern, sollten Sie zunächst versuchen, sich an contentOffset zu erinnern
scrollViewWillBeginDragging
und contentOffset im Inneren zu überprüfen und zurückzusetzen,scrollViewDidScroll
wenn Sie eine diagonale Bewegung feststellen. Eine andere Strategie besteht darin, contentSize zurückzusetzen, um das Scrollen nur in eine Richtung zu ermöglichen, sobald Sie entschieden haben, in welche Richtung der Finger des Benutzers geht. (UIScrollView scheint ziemlich verzeihend zu sein, wenn es darum geht, mit contentSize und contentOffset aus seinen Delegatenmethoden herumzuspielen.)Wenn das nicht funktioniert entweder oder Ergebnisse in schlampig Visuals, haben Sie außer Kraft zu setzen
touchesBegan
,touchesMoved
etc und nicht vorwärts diagonale Bewegung Ereignisse zu UIScrollView. (Die Benutzererfahrung ist in diesem Fall jedoch nicht optimal, da Sie diagonale Bewegungen ignorieren müssen, anstatt sie in eine einzige Richtung zu zwingen. Wenn Sie sich wirklich abenteuerlustig fühlen, können Sie Ihr eigenes UITouch-Lookalike schreiben, etwa RevengeTouch. Ziel -C ist einfach altes C, und es gibt nichts Entenartigeres auf der Welt als C: Solange niemand die reale Klasse der Objekte überprüft, was meiner Meinung nach niemand tut, können Sie jede Klasse wie jede andere Klasse aussehen lassen. Dies öffnet sich eine Möglichkeit, beliebige Berührungen mit beliebigen Koordinaten zu synthetisieren.)Sicherungsstrategie: Es gibt TTScrollView, eine vernünftige Neuimplementierung von UIScrollView in der Three20-Bibliothek . Leider fühlt es sich für den Benutzer sehr unnatürlich und nicht iphonisch an. Wenn jedoch jeder Versuch, UIScrollView zu verwenden, fehlschlägt, können Sie auf eine benutzerdefinierte Bildlaufansicht zurückgreifen. Ich empfehle dagegen, wenn es überhaupt möglich ist; Durch die Verwendung von UIScrollView wird sichergestellt, dass Sie das native Erscheinungsbild erhalten, unabhängig davon, wie es sich in zukünftigen iPhone OS-Versionen entwickelt.
Okay, dieser kleine Aufsatz wurde etwas zu lang. Nach der Arbeit an ScrollingMadness vor einigen Tagen beschäftige ich mich immer noch mit UIScrollView-Spielen.
PS Wenn Sie eines davon zum Laufen bringen und Lust haben, es zu teilen, senden Sie mir bitte eine E-Mail mit dem entsprechenden Code an [email protected]. Ich würde ihn gerne in meine ScrollingMadness- Trickkiste aufnehmen .
PPS Hinzufügen dieses kleinen Aufsatzes zu ScrollingMadness README.
quelle
Das funktioniert bei mir jedes Mal ...
quelle
Für die faulen Leute wie mich:
Set
scrollview.directionalLockEnabled
zuYES
quelle
Ich habe die folgende Methode verwendet, um dieses Problem zu lösen. Ich hoffe, es hilft Ihnen.
Definieren Sie zunächst die folgenden Variablen in der Header-Datei Ihres Controllers.
startPos behält den contentOffset- Wert bei, wenn Ihr Delegat die scrollViewWillBeginDragging- Nachricht empfängt . Bei dieser Methode machen wir das also;
Anschließend verwenden wir diese Werte, um die vom Benutzer beabsichtigte Bildlaufrichtung in der Nachricht scrollViewDidScroll zu bestimmen .
Schließlich müssen wir alle Aktualisierungsvorgänge stoppen, wenn der Benutzer das Ziehen beendet.
quelle
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (decelerate) { scrollDirection=3; } }
geändert zu- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (!decelerate) { scrollDirection=0; } }
und- (void)scrollViewDidEndDecelerating{ scrollDirection=0; }
Ich bin hier vielleicht am Grab, aber ich bin heute auf diesen Beitrag gestoßen, als ich versuchte, das gleiche Problem zu lösen. Hier ist meine Lösung, scheint großartig zu funktionieren.
Ich möchte einen Bildschirm mit Inhalten anzeigen, aber dem Benutzer erlauben, zu einem von vier anderen Bildschirmen zu scrollen, oben links und rechts. Ich habe eine einzelne UIScrollView mit der Größe 320x480 und einer Inhaltsgröße von 960x1440. Der Inhaltsoffset beginnt bei 320.480. In scrollViewDidEndDecelerating: zeichne ich die 5 Ansichten (mittlere Ansichten und die 4 um sie herum) neu und setze den Inhaltsversatz auf 320.480 zurück.
Hier ist das Fleisch meiner Lösung, die scrollViewDidScroll: -Methode.
quelle
Leute, es ist so einfach, die Höhe der Unteransicht mit der Höhe der Bildlaufansicht und der Höhe der Inhaltsgröße gleichzusetzen. Dann ist das vertikale Scrollen deaktiviert.
quelle
Hier ist meine Implementierung einer Seite
UIScrollView
, die nur orthogonale Schriftrollen zulässt. Achten Sie darauf einstellenpagingEnabled
zuYES
.PagedOrthoScrollView.h
PagedOrthoScrollView.m
quelle
Ich musste auch dieses Problem lösen, und obwohl Andrey Tarantsovs Antwort definitiv viele nützliche Informationen enthält, um zu verstehen, wie es
UIScrollViews
funktioniert, halte ich die Lösung für etwas überkompliziert . Meine Lösung, die keine Unterklassen oder Touch-Weiterleitung umfasst, lautet wie folgt:UIPanGestureRecognizers
, wieder einen für jede Richtung.UIScrollViews
Fehlerabhängigkeiten zwischen den Eigenschaften 'panGestureRecognizer' und dem DummyUIPanGestureRecognizer
, der der Richtung senkrecht zur gewünschten Bildlaufrichtung Ihres UIScrollView entsprichtUIGestureRecognizer's
-requireGestureRecognizerToFail:
.-gestureRecognizerShouldBegin:
Berechnen Sie in den Rückrufen für Ihre Dummy-UIPanGestureRecognizers die Anfangsrichtung des Schwenks mit dem Selektor -translationInView: der Geste (IhrUIPanGestureRecognizers
rufen diesen Delegaten-Rückruf erst auf, wenn Ihre Berührung genug übersetzt hat, um sich als Pfanne zu registrieren). Ermöglichen oder verbieten Sie den Beginn Ihrer Gestenerkennung in Abhängigkeit von der berechneten Richtung, die wiederum steuern soll, ob der senkrechte UIScrollView-Pan-Gestenerkenner beginnen darf oder nicht.-gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
nicht erlauben DummyUIPanGestureRecognizers
neben Scrollen (es sei denn , Sie haben einige zusätzliche Verhalten , das Sie hinzufügen wollten) zu laufen.quelle
Ich habe ein Paging-UIScrollView mit einem Rahmen erstellt, der der Größe des Ansichtsbildschirms entspricht, und die Inhaltsgröße auf die für alle meine Inhalte erforderliche Breite und eine Höhe von 1 festgelegt (danke Tonetel). Dann habe ich Menü-UIScrollView-Seiten mit Frames erstellt, die auf jede Seite, die Größe des Ansichtsbildschirms und die Inhaltsgröße der einzelnen Seiten auf die Breite des Bildschirms und die erforderliche Höhe für jede Seite eingestellt sind. Dann habe ich einfach jede der Menüseiten zur Paging-Ansicht hinzugefügt. Stellen Sie sicher, dass das Paging in der Paging-Bildlaufansicht aktiviert, in den Menüansichten jedoch deaktiviert ist (sollte standardmäßig deaktiviert sein), und Sie können loslegen.
Die Paging-Bildlaufansicht scrollt jetzt horizontal, jedoch aufgrund der Inhaltshöhe nicht vertikal. Jede Seite wird aufgrund der Einschränkungen der Inhaltsgröße vertikal, aber nicht horizontal gescrollt.
Hier ist der Code, wenn diese Erklärung zu wünschen übrig lässt:
quelle
Danke Tonetel. Ich habe Ihren Ansatz leicht geändert, aber es war genau das, was ich brauchte, um horizontales Scrollen zu verhindern.
quelle
Dies ist eine verbesserte Lösung von icompot, die für mich ziemlich instabil war. Dieser funktioniert gut und ist einfach zu implementieren:
quelle
Zum späteren Nachschlagen habe ich auf der Antwort von Andrey Tanasov aufgebaut und die folgende Lösung gefunden, die gut funktioniert.
Definieren Sie zunächst eine Variable zum Speichern des contentOffset und setzen Sie sie auf 0,0 Koordinaten
Implementieren Sie nun Folgendes in den scrollView-Delegaten:
Hoffe das hilft.
quelle
Aus den Dokumenten:
"Der Standardwert ist NEIN. Dies bedeutet, dass das Scrollen sowohl in horizontaler als auch in vertikaler Richtung zulässig ist. Wenn der Wert JA lautet und der Benutzer mit dem Ziehen in eine allgemeine Richtung (horizontal oder vertikal) beginnt, deaktiviert die Bildlaufansicht das Scrollen in die andere Richtung. ""
Ich denke, der wichtige Teil ist "wenn ... der Benutzer beginnt , in eine allgemeine Richtung zu ziehen". Wenn sie also anfangen, diagonal zu ziehen, ist dies kein Problem. Nicht, dass diese Dokumente bei der Interpretation immer zuverlässig sind - aber das scheint zu dem zu passen, was Sie sehen.
Das scheint eigentlich sinnvoll. Ich muss fragen - warum wollen Sie sich zu jeder Zeit nur auf horizontal oder nur vertikal beschränken? Vielleicht ist UIScrollView nicht das richtige Tool für Sie?
quelle
Meine Ansicht besteht aus 10 horizontalen Ansichten, und einige dieser Ansichten bestehen aus mehreren vertikalen Ansichten, sodass Sie ungefähr Folgendes erhalten:
Mit aktiviertem Paging sowohl auf der horizontalen als auch auf der vertikalen Achse
Die Ansicht weist außerdem die folgenden Attribute auf:
Gehen Sie nun folgendermaßen vor, um ein diagonales Scrollen zu verhindern:
Funktioniert wie ein Charme für mich!
quelle
_isHorizontalScroll
Müssen intouchesEnded
und auf NEIN zurückgesetzt werdentouchesCancelled
.quelle
Wenn Sie eine große Bildlaufansicht haben ... und das diagonale Scrollen einschränken möchten http://chandanshetty01.blogspot.in/2012/07/restricting-diagonal-scrolling-in.html
quelle
Wenn Sie dies in der Beta-Version 3.0 noch nicht versucht haben, empfehlen wir Ihnen, es einmal auszuprobieren. Ich verwende derzeit eine Reihe von Tabellenansichten in einer Bildlaufansicht und es funktioniert genau wie erwartet. (Nun, das Scrollen funktioniert sowieso. Diese Frage wurde gefunden, als nach einer Lösung für ein völlig anderes Problem gesucht wurde ...)
quelle
Für diejenigen, die in Swift die zweite Lösung von @AndreyTarantsov suchen, ist dies im Code wie folgt:
Was wir hier tun, ist, die aktuelle Position kurz vor dem Ziehen der Bildlaufansicht beizubehalten und dann zu überprüfen, ob x im Vergleich zur aktuellen Position von x zu- oder abgenommen hat. Wenn ja, setzen Sie pagingEnabled auf true, wenn nein (y hat zugenommen / abgenommen), setzen Sie pagingEnabled auf false
Hoffe das ist nützlich für Neuankömmlinge!
quelle
Ich habe es geschafft, dieses Problem zu lösen, indem ich scrollViewDidBeginDragging (_ :) implementiert und die Geschwindigkeit auf dem zugrunde liegenden UIPanGestureRecognizer untersucht habe.
NB: Bei dieser Lösung wird jede Pfanne, die diagonal gewesen wäre, von der scrollView ignoriert.
quelle
Wenn Sie den Interface Builder zum Entwerfen Ihrer Schnittstelle verwenden, ist dies recht einfach.
Klicken Sie im Interface Builder einfach auf UIScrollView und wählen Sie die Option (im Inspektor) aus, die das Scrollen nur in eine Richtung beschränkt.
quelle