Synchronisieren Sie die Transformationsmatrix von Superview und einzelnen Ansichten in verschiedenen Koordinatenräumen

8

Gegebene folgende Hierarchie:

%Vor%

Aufgabe:

Die Kreuze von superview und subview müssen bei einer globalen Transformation immer ausgerichtet sein. Weitere Details im Abschnitt "Anforderungen".

Kontext:

Die obige Ansichtshierarchie gehört zu einem Diagramm. Um maximale Flexibilität zu bieten, erlaubt es, Chartpunkte & amp; verwandte Inhalte auf 3 verschiedene Arten:

  1. Zeichnen in der Basisansicht des Diagramms ( superview ) draw method.

  2. Hinzufügen von Unteransichten zu subview . subview wird auf Zoom / Pan und damit automatisch auf seine Subviews transformiert.

  3. Hinzufügen von Untersichten zu einem gleichgeordneten Element von subview . Aus Gründen der Einfachheit nicht in der Ansichtshierarchie dargestellt und weil es nicht mit dem Problem verbunden ist. Erwähnen Sie es hier nur, um einen Überblick zu geben. Der Unterschied zwischen dieser Methode und 2. besteht darin, dass hier die Ansicht nicht transformiert wird, so dass es der Implementierung des Inhalts überlassen wird, die Transformation aller untergeordneten Elemente "manuell" zu aktualisieren.

Maximale Flexibilität! Aber dadurch entstehen die Kosten, die es ein bisschen schwierig zu implementieren ist. Insbesondere Punkt 2.

Gegenwärtig arbeite ich mit Zoom / Pan, indem ich die Transformationen für superview core graphics drawing und subview getrennt verarbeite, aber dies führt zu Redundanz und Fehleranfälligkeit, z. wiederholter Code für Grenzkontrollen usw.

Nun versuche ich es zu refaktorisieren, um eine globale Matrix zu verwenden, um alle Transformationen zu speichern und daraus alles abzuleiten. Das Anwenden der globalen Matrix auf die Koordinaten, die von superview zum Zeichnen verwendet werden, ist trivial, aber das Ableiten der Matrix von subview , unter Berücksichtigung der Anforderungen, die im nächsten Abschnitt aufgeführt sind, nicht so sehr.

Ich erwähne "Kreuze" in der Ansichtshierarchie, weil ich das auf meinen Spielplätzen als vereinfachte Darstellung eines Diagrammpunkts (mit x / y-Richtlinien) verwende (Sie können nach Bildern und Aufrissen nach unten blättern).

Anforderungen:

  1. Der Inhalt kann gezoomt und geschwenkt werden.
  2. Die Kreuze bleiben immer perfekt ausgerichtet.
  3. subview 's Unteransichten, d. h. die Kreuzlinienansichten können nicht berührt werden (z. B. um Transformationen auf sie anzuwenden) - alles, was modifiziert werden kann, ist subview ' s transform.
  4. Die Zoom- und Panning-Transformationen werden nur in einer globalen Matrix matrix .
  5. gespeichert
  6. Mit matrix werden dann die Koordinaten des gezeichneten Kreuzes in superview (trivial), sowie die Transformationsmatrix von subview (nicht trivial - Grund dieser Frage) berechnet.
    • Da es nicht möglich ist, die Matrix von subview eindeutig von der globalen Matrix abzuleiten, ist es erlaubt, zusätzliche Daten in Variablen zu speichern, die dann zusammen mit der globalen Matrix verwendet werden, um subview 'zu berechnen. s Matrix.
  7. Die Größe / der Ursprung von container kann sich beim Zoomen / Schwenken ändern. Der Grund dafür ist, dass die Beschriftungen der Y-Achse unterschiedliche Längen haben können und dass die Tabelle die Inhaltsgröße dynamisch an den von den Beschriftungen belegten Platz anpassen muss (während des Zoomens und Schwenkens).
  8. Wenn sich die Größe von container ändert, muss natürlich das Verhältnis der Bildschirmkoordinaten der Domäne entsprechend geändert werden, sodass die vollständige ursprüngliche sichtbare Domäne weiterhin in container enthalten ist. ZB wenn ich eine x-Achse mit einer Domain [0, 10] in einem Container-Frame mit einer Breite von 500pt anzeigen würde, dh das Verhältnis zum Konvertieren eines Domain-Punktes zu Bildschirmkoordinaten ist 500/10=50 und verkleinern die Containerbreite auf 250, jetzt meine Domain [0, 10], die in diese neue Breite passen muss, hat ein Verhältnis von 25.
  9. Es muss auch für mehrere Kreuze (zur gleichen Zeit) und beliebige Domain-Standorte für jeden funktionieren. Dies sollte automatisch geschehen, indem 1-7 gelöst wird, aber der Vollständigkeit halber erwähnt wird.

Was ich getan habe:

Hier sind Schritt-für-Schritt-Spielplätze, die ich gemacht habe, um das Problem besser zu verstehen:

Schritt 1 (funktioniert):

Erstellen Sie die Hierarchie, wie am Anfang beschrieben, und zeigen Sie nur Kreuze an, die während des (programmatischen) Zooms & amp; Pfanne. Erfüllt die Anforderungen 1, 2, 3, 4 und 5:

Gist mit Spielplatz.

Besonderheiten hier:

  • Ich habe container view übersprungen, um es einfach zu halten. subview ist eine direkte Unteransicht von superview .
  • subview hat die gleiche Größe wie superview (vor dem Zoomen natürlich), um es auch einfach zu halten.
  • Ich setze den Ankerpunkt von subview auf den Ursprung (0, 0), was notwendig ist, um mit der globalen Matrix synchron zu sein.
  • Die für den Ankerwechsel verwendete Übersetzung muss beibehalten werden, um sie zusammen mit der globalen Matrix wieder anzuwenden. Sonst wird es überschrieben. Dazu benutze ich die Variable subviewAnchorTranslation . Dies gehört zu den zusätzlichen Daten, die ich im Bullet unter Anforderung 5 im Hinterkopf hatte.

Ok, wie du siehst, funktioniert hier alles. Zeit, um den nächsten Schritt zu versuchen.

Schritt 2 (funktioniert):

Eine Kopie von Schritt 1 Spielplatz mit Modifikationen:

  • container view hinzugefügt, ähnelt nun der zu Beginn beschriebenen Ansichtshierarchie.
  • Damit subview , das nun eine Unteransicht von container ist, weiterhin an der gleichen Position angezeigt wird, muss es um -container.origin .
  • nach oben und links verschoben werden
  • Jetzt werden die Zoom- und Schwenkaufrufe zufällig mit Aufrufen verschachtelt, um die Rahmenposition / Größe des Containers zu ändern.

Die Kreuze sind weiterhin synchron. Anforderungen erfüllt: Alle von Schritt 1 + Anforderung 6. Gist mit Spielplatz

Schritt 3 (funktioniert nicht):

Bisher habe ich mit einem Bildschirmbereich gearbeitet, der bei 0 beginnt (linke Seite des sichtbaren Spielplatzergebnisses). Dies bedeutet, dass container nicht die Funktion erfüllt, den Bereich zu enthalten, d. H. Anforderung 7. Um dies zu erfüllen, muss der Ursprung von container in die Verhältnisberechnung einbezogen werden.

Jetzt muss auch subview skaliert werden um in container zu passen / das Kreuz an der richtigen Stelle anzuzeigen. Das fügt eine zweite Variable hinzu (zuerst subviewAnchorTranslation ), die ich contentScalingFactor genannt habe und die diese Skalierung enthält, die in die Matrixberechnung von subview einbezogen werden muss.

Hier habe ich mehrere Experimente gemacht, die alle fehlgeschlagen sind. Im aktuellen Status beginnt subview mit demselben Frame wie container und sein Frame wird angepasst + skaliert, wenn sich der Frame von container ändert. Auch wenn subview jetzt innerhalb des Containers ist, dh sein Ursprung ist nun container 's Ursprung und nicht superview ' s Ursprung, ich muss seinen Anker so updaten, dass der Ursprung nicht bei (0,0) ist (-x, -y), wobei x und y die Koordinaten des Ursprungs von container sind, so dass subview weiterhin in Bezug auf den Ursprung von superview liegt. Und es erscheint logisch, diesen Anker jedes Mal zu aktualisieren, wenn container seinen Ursprung ändert, da dies die relative Position vom Ursprung von content zum Ursprung von superview ändert.

Ich habe Code dafür hochgeladen - in diesem Fall ein komplettes iOS-Projekt statt nur eines Spielplatzes (ich dachte anfangs, dass es funktionierte und wollte mit tatsächlichen Gesten testen). In dem eigentlichen Projekt arbeite ich an der Transformation funktioniert besser, aber ich konnte den Unterschied nicht finden. Jedenfalls funktioniert es nicht gut, irgendwann gibt es immer kleine Offsets und die Punkte / Kreuze geraten aus dem Takt.

Github-Projekt

Ok, wie löse ich das so, dass alle Bedingungen erfüllt sind. Die Kreuze müssen synchron bleiben, mit kontinuierlichem Zoomen / Schwenken und Ändern des Rahmens von container dazwischen.

    
Ixx 26.12.2016, 23:10
quelle

1 Antwort

0

Die vorliegende Antwort erlaubt es, jede Ansicht in der Child-Hierarchie beliebig zu transformieren. nicht verfolgt die Transformation, konvertiert lediglich einen transformierten Punkt und beantwortet somit die Frage:

Was sind die Koordinaten eines Punktes, der sich in einer Unteransicht im Koordinatensystem einer anderen Ansicht befindet, unabhängig davon, wie stark diese Unteransicht umgewandelt wurde .

Um das Parent vom Clipping Container zu entkoppeln und eine generische Antwort anzubieten, schlage ich vor, sie auf der gleichen Ebene konzeptionell und in zu platzieren andere Reihenfolge visuell (†) :

Verwenden Sie eine gemeinsame Superview

Um das Scrollen, Zoomen oder eine andere Transformation von Kind auf Eltern anzuwenden, gehen Sie durch die gemeinsame Superansicht ( Koordinator in der Beispiel).

Der Ansatz ist der Stapelüberlauf-Antwort sehr ähnlich, wo zwei UIScrollView mit unterschiedlicher Geschwindigkeit scrollen.

>

Beachten Sie, dass die rote und schwarze Haarlinie sich unabhängig von der Position überlappen, die einzelnen Ansichten in der Hierarchie Child transformieren, einschließlich der von Container .


↻ Animation wiedergeben

Code

Koordinatenumrechnung

Vereinfachte Darstellung durch Verwendung eines beliebigen Punktes (50,50) im Koordinatensystem der Ansicht Child (wo dieser Punkt effektiv gezeichnet wird) und Konvertierung in Parent View-System sieht folgendermaßen aus:

%Vor%

Zoom & amp; Container übersetzen

%Vor%

(†) Ich habe Superview und Unteransicht in Parent und Child umbenannt.

    
SwiftArchitect 13.01.2017 05:57
quelle

Tags und Links