Zentrieren einer Ansicht in ihrer Übersicht mithilfe der Visual Format Language

160

Ich habe gerade angefangen, AutoLayout für iOS zu lernen und habe mir Visual Format Language angesehen.

Bis auf eines funktioniert alles einwandfrei: Ich kann einfach keine Ansicht in der Übersicht zentrieren.
Ist dies mit VFL möglich oder muss ich selbst manuell eine Einschränkung erstellen?

Christian Schnorr
quelle

Antworten:

232

Derzeit ist es nicht möglich, eine Ansicht in der Übersicht nur mit VFL zu zentrieren . Es ist jedoch nicht so schwierig, dies mit einer einzelnen VFL-Zeichenfolge und einer einzelnen zusätzlichen Einschränkung (pro Achse) zu tun:

VFL: "|-(>=20)-[view]-(>=20)-|"

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeCenterX
                             relatedBy:NSLayoutRelationEqual
                                toItem:view.superview
                             attribute:NSLayoutAttributeCenterX
                            multiplier:1.f constant:0.f];

Man würde denken, dass Sie dies einfach tun könnten (was ich ursprünglich gedacht und versucht habe, als ich diese Frage sah):

[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=20)-[view(==200)]-(>=20)-|"
                                 options: NSLayoutFormatAlignAllCenterX | NSLayoutFormatAlignAllCenterY
                                 metrics:nil
                                   views:@{@"view" : view}];

Ich habe viele verschiedene Variationen der oben genannten versucht, um sie meinem Willen anzupassen, aber dies scheint nicht auf die Übersicht zuzutreffen, selbst wenn explizit zwei separate VFL-Zeichenfolgen für beide Achsen vorhanden sind ( H:|V:). Ich fing dann an, genau zu isolieren, wann die Optionen es tun auf den VFL erhalten angewandt. Sie scheinen nicht für die Übersicht in der VFL zu gelten und gelten nur für explizite Ansichten, die in der VFL-Zeichenfolge erwähnt werden (was in bestimmten Fällen enttäuschend ist).

Ich hoffe, dass Apple in Zukunft eine neue Option hinzufügt, damit die VFL-Optionen die Übersicht berücksichtigen, auch wenn dies nur dann der Fall ist, wenn es neben der Übersicht in der VFL nur eine einzige explizite Ansicht gibt. Eine andere Lösung könnte eine andere Option sein, die an die VFL übergeben wird und etwa Folgendes sagt:NSLayoutFormatOptionIncludeSuperview .

Unnötig zu erwähnen, dass ich viel über VFL gelernt habe, um diese Frage zu beantworten.

Larsacus
quelle
2
Vielen Dank für diese ausführliche Antwort. Der einzige Grund für mich, VFL zu verwenden, besteht darin, diese hässlichen Teile von Einschränkungen loszuwerden. Schade, dass Apple dies noch nicht unterstützt ...
Christian Schnorr
@larsacus Könnten Sie kommentieren, warum die Antwort unten funktioniert? Es hat mich verblüfft.
Matt G
8
Die Antwort von Evgeny unten funktioniert, weil die Autolayout-Engine |und [superview]als separate interne Entitäten behandelt, obwohl sie dasselbe semantische Objekt darstellen. Durch die Verwendung von [superview]Autolayout wird das Superview-Objekt in seine Ausrichtungsmetriken einbezogen (in dem Fall in seiner Antwort auf den Github-Link ist es die NSLayoutFormatAlignAllCenterY/ -Metrik X).
Larsacus
@larsacus Super, danke!
Matt G
Wissen wir, ob die Einschränkung für die Version von VFL, die mit dem aktuellen iOS 7.x geliefert wird, weiterhin gilt?
Drux
138

Ja, es ist möglich, eine Ansicht in ihrer Übersicht mit Visual Format Language zu zentrieren. Sowohl vertikal als auch horizontal. Hier ist die Demo:

https://github.com/evgenyneu/center-vfl

Evgenii
quelle
15
Das ist großartig, denkst du, du könntest das erweitern und erklären, warum es funktioniert?
Tapi
3
Am Ende habe ich etwas verwendet, das näher an der markierten Antwort liegt, aber +1 für einen Github-Anhang in Ihrem
Rembrandt Q. Einstein
4
@Dan Wenn es bei Ihnen nicht funktioniert, müssen möglicherweise auch Einschränkungen für Breite und Höhe festgelegt werden. Die VFL wäre für die Breite wie folgt: @ "[label (== 200)]" und für die Höhe so: @ "V: [label (== 200)]"
Jonathan Zhan
1
Warum sind die Ergebnisse von | und [Übersicht] anders? Ist das etwas, das radarisiert werden sollte, oder gibt es etwas, dem ich einfach nicht folge?
Matt G
3
Das Einschränkungsformat kann nicht analysiert werden: Die Optionen maskieren, dass Ansichten an einer horizontalen Kante ausgerichtet werden müssen, was für ein ebenfalls horizontales Layout nicht zulässig ist. H: [Übersicht] - (<= 1) - [label1]
Grabner
82

Ich denke, es ist besser, Einschränkungen manuell zu erstellen.

[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1
                               constant:0]];
[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterY
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterY
                             multiplier:1
                               constant:0]];

Jetzt können wir NSLayoutAnchor verwenden , um AutoLayout programmgesteuert zu definieren:

(Verfügbar in iOS 9.0 und höher)

view.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true

Ich empfehle die Verwendung von SnapKit , einem DSL, um das automatische Layout sowohl für iOS als auch für MacOS zu vereinfachen.

view.snp.makeConstraints({ $0.center.equalToSuperview() })
jqgsninimo
quelle
1
Hat in meinem Kontext am besten funktioniert - aber keine VFL.
Brainray
es gibt mir eine Warnung:Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
Tapas Pal
10

Absolut möglich war das so:

[NSLayoutConstraint constraintsWithVisualFormat:@"H:[view]-(<=1)-[subview]"
                                        options:NSLayoutFormatAlignAllCenterY
                                        metrics:nil
                                          views:NSDictionaryOfVariableBindings(view, subview)];

Habe das von hier gelernt . Der Code über der Mitte zeigt offensichtlich die Unteransicht von Y.

Swift3:

        NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=1)-[subview]",
                                       options: .alignAllCenterY,
                                                       metrics: nil,
                                                       views: ["view":self, "subview":_spinnerView])
Igor Muzyka
quelle
7

Fügen Sie den folgenden Code hinzu und platzieren Sie Ihre Schaltfläche in der Mitte des Bildschirms. Absolut möglich.

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterY
                           metrics:0
                           views:views]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterX
                           metrics:0
                           views:views]];
Pankaj Gaikar
quelle
1

Ich weiß, dass du es nicht willst, aber du kannst natürlich die Ränder berechnen und sie verwenden, um die visuelle Formatzeichenfolge zu erstellen;)

Wie auch immer, nein. Leider ist es mit VFL nicht möglich, dies "automatisch" zu tun - zumindest noch nicht.

Tobi
quelle
Nun, Sie können NSString stringWithFormat: verwenden, um Ihren VFL-String zur Laufzeit dynamisch zu erstellen.
TRVD1707
1

Dank der Antwort von @Evgenii erstelle ich ein vollständiges Beispiel im Kern:

Zentrieren Sie das rote Quadrat vertikal mit der benutzerdefinierten Höhe

https://gist.github.com/yallenh/9fc2104b719742ae51384ed95dcaf626

Sie können eine Ansicht vertikal zentrieren, indem Sie entweder VFL (was nicht ganz intuitiv ist) verwenden oder Einschränkungen manuell verwenden. Übrigens kann ich in VFL nicht sowohl centerY als auch height wie folgt kombinieren:

"H:[subView]-(<=1)-[followIcon(==customHeight)]"

In der aktuellen Lösung werden VFL-Einschränkungen separat hinzugefügt.

Allen Huang
quelle
0

Was getan werden muss, ist, dass die Übersicht im Wörterbuch deklariert werden sollte. Anstatt zu verwenden, verwenden |Sie @{@"super": view.superview};.

Und NSLayoutFormatAlignAllCenterXfür vertikal und NSLayoutFormatAlignAllCenterYfür horizontal als Optionen.

Lagos
quelle
0

Sie können zusätzliche Ansichten verwenden.

NSDictionary *formats =
@{
  @"H:|[centerXView]|": @0,
  @"V:|[centerYView]|": @0,
  @"H:[centerYView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterY),
  @"V:[centerXView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterX),
};
NSDictionary *views = @{
  @"centerXView": centerXView, 
  @"centerYView": centerYView, 
  @"yourView": yourView,
};
 ^(NSString *format, NSNumber *options, BOOL *stop) {
     [superview addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:format
                                              options:options.integerValue
                                              metrics:nil
                                                views:views]];
 }];

Wenn du es ordentlich willst, dann centerXView.hidden = centerYView.hidden = YES; .

PS: Ich bin nicht sicher, ob ich die zusätzlichen Ansichten "Platzhalter" nennen kann, da Englisch meine zweite Sprache ist. Es wird geschätzt, wenn mir jemand das sagen kann.

CopperCash
quelle
Etwas fehlt hier. Wie ruft man den Block unter der Views- Deklaration auf? Es ist kein gesetzlicher Code, wie Sie es ausdrücken
ishahak
0

Für diejenigen, die wegen einer Interface Builderbasierten Lösung hierher gekommen sind (Google führt mich hierher), fügen Sie einfach konstante Einschränkungen für Breite und Höhe hinzu und wählen Sie die Unteransicht -> ziehen Sie die Steuerung in die Übersicht -> wählen Sie "Vertikal / horizontal in der Übersicht zentrieren".

superarts.org
quelle
0

Das ist meine Methode

//create a 100*100 rect in the center of superView
var consts = NSLayoutConstraint.constraints(withVisualFormat: "H:|-space-[myView]-space-|", options: [], metrics:["space":view.bounds.width/2-50], views:["myView":myView])

consts += NSLayoutConstraint.constraints(withVisualFormat: "V:|-space-[myView]-space-|", options: [], metrics: ["space":view.bounds.height/2-50], views: ["myView":myView])
Calcifer
quelle
-1

Ich muss nur sagen, dass Sie dies anstelle von Einschränkungen verwenden können:

child.center = [parent convertPoint:parent.center fromView:parent.superview];
Nik Kov
quelle
4
Ich muss nur sagen, das ist nicht das, wonach er fragt, und es wird auch nicht funktionieren, wenn Sie das Gerät drehen, die Größe des Bildschirms
ändern
-1

@ igor-muzyka Antwort in Swift 2 :

NSLayoutConstraint.constraintsWithVisualFormat("H:[view]-(<=1)-[subview]", 
                                               options: .AlignAllCenterY,
                                               metrics: nil,
                                               views: ["view":view, "subview":subview])
Federico Zanetello
quelle
-3

Anstatt VFL zu verwenden, können Sie dies einfach im Interface Builder tun. Klicken Sie im Haupt-Storyboard bei gedrückter Strg-Taste auf die Ansicht, die Sie zentrieren möchten. Ziehen Sie in die Übersicht (bei gedrückter Strg-Taste) und wählen Sie "Mitte X" oder "Mitte Y". Kein Code notwendig.

leenyburger
quelle