Hinweis : Seit diese Frage gestellt wurde, haben sich die Dinge weiterentwickelt. siehe hier für eine gute aktuelle Übersicht.
Vor dem automatischen Layout können Sie den Ankerpunkt der Ebene einer Ansicht ändern, ohne die Ansicht zu verschieben, indem Sie den Rahmen speichern, den Ankerpunkt festlegen und den Rahmen wiederherstellen.
In einer Welt mit automatischem Layout setzen wir keine Frames mehr, aber Einschränkungen scheinen nicht der Aufgabe gewachsen zu sein, die Position einer Ansicht wieder an die gewünschte Stelle anzupassen. Sie können die Einschränkungen hacken, um Ihre Ansicht neu zu positionieren. Bei Rotations- oder anderen Größenänderungsereignissen werden diese jedoch wieder ungültig.
Die folgende gute Idee funktioniert nicht, da sie eine "ungültige Paarung von Layoutattributen (links und Breite)" erstellt:
layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:layerView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:20.0]];
Ich wollte hier den linken Rand layerView
der Ansicht mit dem angepassten Ankerpunkt auf die Hälfte ihrer Breite plus 20 einstellen (den Abstand, den ich vom linken Rand der Übersicht einfügen möchte).
Ist es möglich, den Ankerpunkt in einer Ansicht mit automatischem Layout zu ändern, ohne die Position einer Ansicht zu ändern? Muss ich fest codierte Werte verwenden und die Einschränkung bei jeder Drehung bearbeiten? Ich hoffe nicht.
Ich muss den Ankerpunkt ändern, damit ich beim Anwenden einer Transformation auf die Ansicht den richtigen visuellen Effekt erhalte.
quelle
layerView
weiß seine Breite? Klebt es mit der rechten Seite an etwas anderem?//Size-related constraints that work fine
Dies wird in der - Breite und Höhe der Ebenenansicht von der Übersichtsansicht abgeleitet.Antworten:
[BEARBEITEN: Warnung: Die gesamte nachfolgende Diskussion wird möglicherweise durch iOS 8 veraltet oder zumindest stark gemildert, was möglicherweise nicht mehr den Fehler macht, das Layout zum Zeitpunkt der Anwendung einer Ansichtstransformation auszulösen.]
Autolayout vs. View-Transformationen
Autolayout spielt bei Ansichtstransformationen überhaupt nicht gut. Der Grund ist, soweit ich das beurteilen kann, dass Sie sich nicht mit dem Rahmen einer Ansicht herumschlagen sollen, die eine Transformation enthält (außer der Standardidentitätstransformation) - aber genau das macht Autolayout. Die Art und Weise, wie Autolayout funktioniert, besteht darin, dass
layoutSubviews
zur Laufzeit alle Einschränkungen durchlaufen und die Frames aller Ansichten entsprechend festgelegt werden.Mit anderen Worten, die Einschränkungen sind keine Magie; Sie sind nur eine To-Do-Liste.
layoutSubviews
Hier wird die To-Do-Liste erstellt. Und das durch Setzen von Frames.Ich kann nicht anders, als dies als Fehler zu betrachten. Wenn ich diese Transformation auf eine Ansicht anwende:
Ich erwarte, dass die Ansicht mit ihrer Mitte an derselben Stelle wie zuvor und in halber Größe angezeigt wird. Aber je nach den Einschränkungen sehe ich das möglicherweise überhaupt nicht.
[Eigentlich gibt es hier eine zweite Überraschung: Das Anwenden einer Transformation auf eine Ansicht löst sofort das Layout aus. Dies scheint mir ein weiterer Fehler zu sein. Oder vielleicht ist es das Herzstück des ersten Fehlers. Was ich erwarten würde, ist, dass ich zumindest bis zur Layoutzeit mit einer Transformation davonkommen kann, z. B. wenn das Gerät gedreht wird - genauso wie ich mit einer Rahmenanimation bis zur Layoutzeit davonkommen kann. Tatsächlich ist die Layoutzeit jedoch unmittelbar, was einfach falsch erscheint.]
Lösung 1: Keine Einschränkungen
Eine aktuelle Lösung besteht darin, alle Einschränkungen, die sie betreffen, zu entfernen, wenn ich eine semipermanente Transformation auf eine Ansicht anwenden möchte (und sie nicht nur vorübergehend wackeln möchte). Leider führt dies normalerweise dazu, dass die Ansicht vom Bildschirm verschwindet, da das automatische Layout immer noch stattfindet und es jetzt keine Einschränkungen gibt, die uns mitteilen, wo die Ansicht platziert werden soll. Zusätzlich zum Entfernen der Einschränkungen habe ich die Ansicht
translatesAutoresizingMaskIntoConstraints
auf JA gesetzt. Die Ansicht funktioniert jetzt auf die alte Art und Weise, ohne dass dies durch Autolayout beeinträchtigt wird. (Es ist offensichtlich von Autolayout betroffen, aber die impliziten Einschränkungen der Autoresize-Maske führen dazu, dass sein Verhalten genau so ist, wie es vor dem Autolayout war.)Lösung 2: Verwenden Sie nur geeignete Einschränkungen
Wenn dies etwas drastisch erscheint, besteht eine andere Lösung darin, die Einschränkungen so festzulegen, dass sie mit einer beabsichtigten Transformation korrekt funktionieren. Wenn eine Ansicht nur nach ihrer internen festen Breite und Höhe dimensioniert und beispielsweise nur nach ihrer Mitte positioniert wird, funktioniert meine Skalentransformation wie erwartet. In diesem Code entferne ich die vorhandenen Einschränkungen für eine Unteransicht (
otherView
) und ersetze sie durch diese vier Einschränkungen, indem ich ihr eine feste Breite und Höhe gebe und sie nur durch ihre Mitte fixiere . Danach funktioniert meine Skalentransformation:Das Ergebnis ist, dass das automatische Layout den Rahmen der Ansicht nicht berührt, wenn Sie keine Einschränkungen haben, die sich auf den Rahmen einer Ansicht auswirken. Dies ist genau das, wonach Sie suchen, wenn eine Transformation beteiligt ist.
Lösung 3: Verwenden Sie eine Unteransicht
Das Problem bei beiden oben genannten Lösungen besteht darin, dass wir die Vorteile von Einschränkungen verlieren, um unsere Sichtweise zu positionieren. Hier ist eine Lösung, die das löst. Beginnen Sie mit einer unsichtbaren Ansicht, deren Aufgabe es ist, ausschließlich als Host zu fungieren, und verwenden Sie Einschränkungen, um sie zu positionieren. Fügen Sie darin die reale Ansicht als Unteransicht ein. Verwenden Sie Einschränkungen, um die Unteransicht in der Hostansicht zu positionieren, beschränken Sie diese Einschränkungen jedoch auf Einschränkungen, die sich beim Anwenden einer Transformation nicht wehren.
Hier ist eine Illustration:
Die weiße Ansicht ist die Hostansicht. Sie sollen so tun, als wäre es transparent und daher unsichtbar. Die rote Ansicht ist die Unteransicht, die durch Anheften der Mitte an die Mitte der Hostansicht positioniert wird. Jetzt können wir die rote Ansicht problemlos skalieren und um ihre Mitte drehen, und die Abbildung zeigt, dass wir dies getan haben:
In der Zwischenzeit halten die Einschränkungen für die Hostansicht sie beim Drehen des Geräts an der richtigen Stelle.
Lösung 4: Verwenden Sie stattdessen Layer-Transformationen
Verwenden Sie anstelle von Ansichtstransformationen Ebenentransformationen, die kein Layout auslösen und daher keinen unmittelbaren Konflikt mit Einschränkungen verursachen.
Zum Beispiel kann diese einfache Animation der "Pochen" -Ansicht unter Autolayout unterbrochen werden:
Auch wenn sich die Größe der Ansicht am Ende nicht geändert hat, wird lediglich
transform
das Layout der Ursachen festgelegt, und Einschränkungen können dazu führen, dass die Ansicht herumspringt. (Fühlt sich das wie ein Fehler an oder was?) Wenn wir jedoch dasselbe mit Core Animation tun (mithilfe einer CABasicAnimation und Anwenden der Animation auf die Ebene der Ansicht), geschieht das Layout nicht und es funktioniert einwandfrei:quelle
Ich hatte eine ähnliche Isuue und hörte gerade Back vom Autolayout-Team bei Apple. Sie schlagen vor, den von Matt vorgeschlagenen Ansatz für die Containeransicht zu verwenden, erstellen jedoch eine Unterklasse von UIView, um layoutSubviews zu überschreiben und dort benutzerdefinierten Layoutcode anzuwenden. Dies funktioniert wie ein Zauber
Die Header-Datei sieht so aus, dass Sie die gewünschte Unteransicht direkt über den Interface Builder verknüpfen können
und die m-Datei wendet den speziellen Code folgendermaßen an:
Wie Sie sehen können, wird beim ersten Aufruf der Mittelpunkt der Ansicht erfasst und diese Position bei weiteren Aufrufen wiederverwendet, um die Ansicht entsprechend zu platzieren. Dies überschreibt den Autolayout-Code in dem Sinne, dass er nach [super layoutSubviews] stattfindet; welches Autolayout-Code enthält.
Auf diese Weise muss Autolayout nicht mehr vermieden werden. Sie können jedoch ein eigenes Autolayout erstellen, wenn das Standardverhalten nicht mehr angemessen ist. Natürlich können Sie viel kompliziertere Dinge anwenden als in diesem Beispiel, aber das war alles, was ich brauchte, da meine App nur den Porträtmodus verwenden kann.
quelle
layoutSubviews
. Ich möchte nur hinzufügen, wenn auch, dass Apple hier nur macht Sie tun , was ich denke , sie sollten (bei der Umsetzung von Autolayout) getan haben , die ganze Zeit .Ich finde einen einfachen Weg. Und es funktioniert unter iOS 8 und iOS 9.
Wie Ankerpunkt anpassen, wenn Sie rahmenbasiertes Layout verwenden:
Wenn Sie den Anker der Ansicht mit automatischem Layout anpassen, tun Sie dasselbe, jedoch auf Einschränkungen. Wenn sich anchorPoint von (0,5, 0,5) auf (1, 0,5) ändert, bewegt sich die Ebenenansicht mit einem Abstand von der halben Länge der Ansichtsbreite nach links. Sie müssen dies also kompensieren.
Ich verstehe die Einschränkung in der Frage nicht. Nehmen wir also an, dass Sie eine centerX-Einschränkung relativ zu superView centerX mit einer Konstanten hinzufügen: layerView.centerX = superView.centerX + Konstante
quelle
Wenn Sie das automatische Layout verwenden, sehe ich nicht, wie das manuelle Festlegen der Position auf lange Sicht funktioniert, da das automatische Layout möglicherweise den Positionswert beeinträchtigt, den Sie bei der Berechnung des eigenen Layouts festgelegt haben.
Vielmehr müssen die Layouteinschränkungen selbst geändert werden, um die durch das Setzen des Ankerpunkts hervorgerufenen Änderungen auszugleichen. Die folgende Funktion erledigt dies für nicht transformierte Ansichten.
Ich gebe zu, dass dies wahrscheinlich nicht alles ist, was Sie sich erhofft haben, da der einzige Grund, warum Sie den anchorPoint ändern möchten, normalerweise darin besteht, eine Transformation festzulegen. Dies würde eine komplexere Funktion erfordern, die die Layoutbeschränkungen aktualisiert, um alle Rahmenänderungen widerzuspiegeln , die durch die Transformationseigenschaft selbst verursacht werden könnten. Dies ist schwierig, da Transformationen viel zum Frame beitragen können. Eine Skalierungs- oder Rotationstransformation würde den Rahmen größer machen, sodass wir alle Breiten- oder Höhenbeschränkungen usw. aktualisieren müssten.
Wenn Sie die Transformation nur für eine temporäre Animation verwenden, kann das oben Genannte ausreichen, da ich nicht glaube, dass das automatische Layout verhindert, dass die Animation während des Flugs Bilder darstellt, die rein vorübergehende Verstöße gegen die Einschränkungen darstellen.
quelle
tl: dr: Sie können eine Steckdose für eine der Einschränkungen erstellen, damit diese entfernt und wieder hinzugefügt werden kann.
Ich habe ein neues Projekt erstellt und eine Ansicht mit einer festen Größe in der Mitte hinzugefügt. Die Einschränkungen sind in der Abbildung unten dargestellt.
Als nächstes habe ich einen Auslass für die Ansicht hinzugefügt, die gedreht werden soll, und für die Einschränkung der Ausrichtung von Mitte x.
Später
viewDidAppear
berechne ich den neuen AnkerpunktDann entferne ich die Einschränkung, für die ich eine Steckdose habe, erstelle eine neue, die versetzt ist, und füge sie wieder hinzu. Danach sage ich der Ansicht mit der geänderten Einschränkung, dass sie die Einschränkungen aktualisieren muss.
Zum Schluss füge ich einfach die Rotationsanimation zur rotierenden Ansicht hinzu.
Die rotierende Ebene sieht so aus, als ob sie zentriert bleibt (was sie sollte), auch wenn das Gerät gedreht wird oder auf andere Weise die Einschränkungen aktualisiert werden. Die neue Einschränkung und der geänderte Ankerpunkt heben sich visuell auf.
quelle
Meine aktuelle Lösung besteht darin, die Position der Ebene in manuell anzupassen
viewDidLayoutSubviews
. Dieser Code kann auchlayoutSubviews
für eine Ansichtsunterklasse verwendet werden. In meinem Fall handelt es sich bei meiner Ansicht jedoch um eine Ansicht der obersten Ebene innerhalb eines Ansichtscontrollers. Daher musste ich keine UIView-Unterklasse erstellen.Es scheint zu viel Aufwand zu sein, daher sind andere Antworten sehr willkommen.
quelle
Inspiriert von der Antwort meiner Matte, beschloss ich, einen anderen Ansatz zu versuchen. Eine Containeransicht mit entsprechend angewendeten Einschränkungen kann verwendet werden. Die Ansicht mit dem geänderten Ankerpunkt kann dann mithilfe von Masken zur automatischen Größenanpassung und expliziten Rahmeneinstellung wie in den schlechten alten Zeiten in der Containeransicht platziert werden.
Für meine Situation ist es sowieso ein Vergnügen. Die Ansichten werden hier in viewDidLoad eingerichtet:
Es spielt keine Rolle, dass die Rahmen für die rote Ansicht zu diesem Zeitpunkt Null sind, da die automatische Größenmaske in der grünen Ansicht angezeigt wird.
Ich habe eine Rotationstransformation für eine Aktionsmethode hinzugefügt, und dies war das Ergebnis:
Es schien sich während der Gerätedrehung von selbst zu verlieren, daher habe ich dies der viewDidLayoutSubviews-Methode hinzugefügt:
quelle
view.layer
Ruhe zu lassen und all Ihre Arbeit in einer Unterschicht von zu erledigenview.layer
. Mit anderen Worten, die Ansicht wäre nur ein Host, und alle Transformationen von Zeichnungen und Unterschichten usw. wären eine Ebene tiefer, die von Einschränkungen nicht beeinflusst wird.Ich denke, Sie vereiteln mit dieser Methode den Zweck des Autolayouts. Sie haben erwähnt, dass die Breite und der rechte Rand von der Übersicht abhängen. Warum also nicht einfach Einschränkungen entlang dieser Denkrichtung hinzufügen?
Verlieren Sie das anchorPoint / transform-Paradigma und versuchen Sie:
Die
NSLayoutAttributeRight
Einschränkung bedeutet genau wieanchorPoint = CGPointMake(1.0, 0.5)
, und dieNSLayoutAttributeWidth
Einschränkung entspricht in etwa der Ihres vorherigen CodesNSLayoutAttributeLeft
.quelle
anchorPoint
. Mein Punkt ist, dass Sie es nicht für Messungen verwenden sollten. Das UIView-Autolayout-System sollte unabhängig von den CALayer-Transformationen sein. Also UIView: Layout, CALayer: Aussehen / AnimationenviewDidLayoutSubviews
sollte das beheben;position
geht immer mitanchorPoint
. Meine Antwort zeigt lediglich, wie die Einschränkung für die Identitätstransformation definiert wird.Diese Frage und Antworten haben mich dazu inspiriert, meine eigenen Probleme mit Autolayout und Skalierung zu lösen, aber mit Bildlaufansichten. Ich habe ein Beispiel für meine Lösung auf Github erstellt:
https://github.com/hansdesmedt/AutoLayout-scrollview-scale
Dies ist ein Beispiel für eine UIScrollView mit benutzerdefiniertem Paging, das vollständig in AutoLayout erstellt wurde und mit langem Drücken und Tippen zum Zoomen skalierbar ist (CATransform3DMakeScale). iOS 6 und 7 kompatibel.
quelle
Es ist ein großes Thema und ich habe nicht alle Kommentare gelesen, war aber mit dem gleichen Problem konfrontiert.
Ich hatte einen Blick von XIB mit Autolayout. Und ich wollte seine Transformationseigenschaft aktualisieren. Das Einbetten der Ansicht in eine Containeransicht löst mein Problem nicht, da das automatische Layout auf die Containeransicht seltsam wirkte. Aus diesem Grund habe ich gerade die zweite Containeransicht hinzugefügt, um die Containeransicht zu enthalten, die meine Ansicht enthält, und Transformationen darauf angewendet.
quelle
Angenommen, Sie haben den Ankerpunkt auf (0, 0) geändert. Der Ankerpunkt befindet sich jetzt oben links. Immer , wenn Sie das Wort sehen Zentrum in Auto - Layout, sollten Sie denken , oben links .
Wenn Sie Ihren anchorPoint anpassen, ändern Sie einfach die Semantik von AutoLayout. Das automatische Layout beeinträchtigt weder Ihren anchorPoint noch umgekehrt. Wenn Sie das nicht verstehen, werden Sie eine schlechte Zeit haben .
Beispiel:
Abbildung A. Keine Ankerpunktänderungen
Abbildung B. Der Ankerpunkt wurde nach links oben geändert
Abbildung A und Abbildung B sehen genau gleich aus. Nichts hat sich geändert. Nur die Definition dessen, worauf sich das Zentrum bezieht, hat sich geändert.
quelle