Angenommen, ich habe in UIStackView weitere Ansichten hinzugefügt, die angezeigt werden können. Wie kann ich den UIStackView
Bildlauf durchführen?
Angenommen, ich habe in UIStackView weitere Ansichten hinzugefügt, die angezeigt werden können. Wie kann ich den UIStackView
Bildlauf durchführen?
Antworten:
Für den Fall, dass jemand nach einer Lösung ohne Code sucht , habe ich ein Beispiel erstellt, um dies mithilfe des automatischen Layouts vollständig im Storyboard zu tun.
Sie können es von Github bekommen .
Grundsätzlich, um das Beispiel neu zu erstellen (für vertikales Scrollen):
UIScrollView
und legen Sie die Einschränkungen fest.UIStackView
zum hinzuUIScrollView
Leading
, Trailing
, Top
und Bottom
sollten auf diejenigen gleich ausUIScrollView
Width
Einschränkung zwischen UIStackView
und ein UIScrollView
.UIStackView
UIViews
auf dieUIStackView
Austausch Width
für die Height
in Schritt 4 und Satz Axis
= Horizontal
in Schritt 5 eine horizontale UIStackView zu erhalten.
Apples Auto Layout Guide enthält einen ganzen Abschnitt über das Arbeiten mit Bildlaufansichten . Einige relevante Ausschnitte:
- Befestigen Sie die oberen, unteren, vorderen und hinteren Kanten der Inhaltsansicht an den entsprechenden Kanten der Bildlaufansicht. Die Inhaltsansicht definiert jetzt den Inhaltsbereich der Bildlaufansicht.
- (Optional) Um das horizontale Scrollen zu deaktivieren, stellen Sie die Breite der Inhaltsansicht gleich der Breite der Bildlaufansicht ein. Die Inhaltsansicht füllt jetzt die Bildlaufansicht horizontal aus.
- (Optional) Um das vertikale Scrollen zu deaktivieren, stellen Sie die Höhe der Inhaltsansicht auf die Höhe der Bildlaufansicht ein. Die Inhaltsansicht füllt jetzt die Bildlaufansicht horizontal aus.
Außerdem:
Ihr Layout muss die Größe der Inhaltsansicht vollständig definieren (außer in den Schritten 5 und 6). … Wenn die Inhaltsansicht größer als die Bildlaufansicht ist, ermöglicht die Bildlaufansicht das vertikale Scrollen. Wenn die Inhaltsansicht breiter als die Bildlaufansicht ist, ermöglicht die Bildlaufansicht das horizontale Scrollen.
Zusammenfassend muss die Inhaltsansicht der Bildlaufansicht (in diesem Fall eine Stapelansicht) an ihren Rändern befestigt werden und ihre Breite und / oder Höhe anderweitig eingeschränkt sein. Dies bedeutet, dass der Inhalt der Stapelansicht (direkt oder indirekt) in der Richtung (den Richtungen) eingeschränkt werden muss, in der das Scrollen gewünscht wird. Dies kann beispielsweise bedeuten, dass jeder Ansicht in einer vertikal scrollenden Stapelansicht eine Höhenbeschränkung hinzugefügt wird. Im Folgenden finden Sie ein Beispiel für das vertikale Scrollen einer Bildlaufansicht mit einer Stapelansicht:
// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
Need constraints for: Y position or height.
Dieser Fehler wird nur behoben, wenn Sie sowohl eine Breiten- als auch eine Höhenbeschränkung für die Stapelansicht festlegen, wodurch das vertikale Scrollen deaktiviert wird. Funktioniert dieser Code noch für Sie?
Die Einschränkungen in der Antwort mit der höchsten Abstimmung hier haben für mich funktioniert, und ich habe ein Bild der Einschränkungen unten eingefügt, die in meinem Storyboard erstellt wurden.
Ich habe jedoch zwei Probleme festgestellt, die anderen bewusst sein sollten:
Nach dem Hinzufügen von Einschränkungen, die denen in der akzeptierten Antwort ähneln, wird der rote Autolayout-Fehler angezeigt Need constraints for: X position or width
. Dies wurde gelöst, indem ein UILabel als Unteransicht der Stapelansicht hinzugefügt wurde.
Ich füge die Unteransichten programmgesteuert hinzu, sodass ich ursprünglich keine Unteransichten im Storyboard hatte. Um die Autolayout-Fehler zu beseitigen, fügen Sie dem Storyboard eine Unteransicht hinzu und entfernen Sie sie beim Laden, bevor Sie Ihre tatsächlichen Unteransichten und Einschränkungen hinzufügen.
Ich habe ursprünglich versucht, UIButtons
UIStackView hinzuzufügen. Die Schaltflächen und Ansichten würden geladen, die Bildlaufansicht jedoch nicht . Dies wurde durch Hinzufügen gelöstUILabels
anstelle von Schaltflächen zur Stapelansicht . Unter Verwendung der gleichen Einschränkungen wird in dieser Ansichtshierarchie mit den UILabels gescrollt, in den UIButtons jedoch nicht.
Ich bin von diesem Problem verwirrt, da die UIButtons Sie scheinen eine IntrinsicContentSize zu haben (wird von der Stapel - Ansicht). Wenn jemand weiß, warum die Tasten nicht funktionieren, würde ich gerne wissen, warum.
Hier ist meine Ansichtshierarchie und Einschränkungen als Referenz:
Wie Eik sagt UIStackView
und gut UIScrollView
zusammen spielt, siehe hier .
Der Schlüssel ist, dass der UIStackView
die variable Höhe / Breite für verschiedene Inhalte handhabt und UIScrollView
dann seine Aufgabe gut erledigt, diesen Inhalt zu scrollen / zu springen:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
Ich wollte das Gleiche tun und bin auf diesen ausgezeichneten Beitrag gestoßen . Wenn Sie dies programmgesteuert mithilfe der Anker-API tun möchten, ist dies der richtige Weg.
Um es zusammenzufassen, binden Sie Ihre UIStackView
in Ihre ein UIScrollView
und legen Sie die Ankereinschränkungen von so fest UIStackView
, dass sie mit denen der folgenden übereinstimmen UIScrollView
:
stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
Haben Sie eine leere Vollbildszene
Fügen Sie eine Bildlaufansicht hinzu. Ziehen Sie bei gedrückter Ctrl-Taste von der Bildlaufansicht in die Basisansicht , fügen Sie links-rechts-oben-unten und alle Null hinzu.
Fügen Sie der Bildlaufansicht eine Stapelansicht hinzu. Ziehen Sie bei gedrückter Ctrl-Taste von der Stapelansicht in die Bildlaufansicht , fügen Sie links-rechts-oben-unten und alle Null hinzu.
Legen Sie zwei oder drei Etiketten in die Stapelansicht.
Machen Sie aus Gründen der Klarheit die Hintergrundfarbe des Etiketts rot. Stellen Sie die Etikettenhöhe auf 100 ein.
Stellen Sie nun die Stellen Sie Breite jedes UILabels ein:
Überraschenderweise Steuertaste von der UILabel
auf die Bildlaufansicht, nicht auf den Stapel Ansicht und wählen gleiche Breiten .
Wiederholen:
So einfach ist das. Das ist das Geheimnis.
Geheimtipp - Apple Bug:
Es wird nicht mit nur einem Artikel funktionieren ! Fügen Sie einige Beschriftungen hinzu, damit die Demo funktioniert.
Du bist fertig.
Tipp: Sie müssen jedem neuen Element eine Höhe hinzufügen . Jedes Element in einer Bildlaufstapelansicht muss entweder eine Eigengröße (z. B. eine Beschriftung) haben oder eine explizite Höhenbeschränkung hinzufügen.
Der alternative Ansatz:
Oben: Stellen Sie überraschenderweise die Breite der UILabels auf die Breite der Bildlaufansicht (nicht der Stapelansicht) ein.
Abwechselnd...
Sie haben also zwei Möglichkeiten:
oder
Um klar zu sein, machen Sie EINE dieser Methoden, machen Sie NICHT beide.
Zum horizontalen Scrollen. Erstellen Sie zunächst ein UIStackView
und ein UIScrollView
und fügen Sie sie folgendermaßen zu Ihrer Ansicht hinzu:
let scrollView = UIScrollView()
let stackView = UIStackView()
scrollView.addSubview(stackView)
view.addSubview(scrollView)
In Erinnerung an die zu setzen , translatesAutoresizingMaskIntoConstraints
um false
auf dem UIStackView
und die UIScrollView
:
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
Damit alles funktioniert, sollten die nachlaufenden, führenden, oberen und unteren Anker UIStackView
der UIScrollView
Anker gleich sein:
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
Aber der Breitenanker des UIStackView
Mosts muss gleich oder größer als die Breite des UIScrollView
Ankers sein:
stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true
Verankern Sie jetzt UIScrollView
zum Beispiel:
scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo:view.trailingAnchor).isActive = true
Als nächstes würde ich vorschlagen, die folgenden Einstellungen für die UIStackView
Ausrichtung und Verteilung zu versuchen :
topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center
topicStackView.spacing = 10
Schließlich müssen Sie die addArrangedSubview:
Methode verwenden, um Ihrem UIStackView Unteransichten hinzuzufügen.
Ein zusätzliches Merkmal , dass Sie nützlich finden könnten , ist , dass , weil die UIStackView
innerhalb einer gehalten wird UIScrollView
nun Sie den Zugriff auf Texteinschübe haben , um die Dinge sehen ein bisschen schöner.
let inset:CGFloat = 20
scrollView.contentInset.left = inset
scrollView.contentInset.right = inset
// remember if you're using insets then reduce the width of your stack view to match
stackView.widthAnchor.constraint(greaterThanOrEqualTo: topicScrollView.widthAnchor, constant: -inset*2).isActive = true
Fügen Sie dies einfach hinzu zu viewdidload
:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
Sie können ScrollableStackView ausprobieren : https://github.com/gurhub/ScrollableStackView
Es ist eine Objective-C- und Swift-kompatible Bibliothek. Es ist über CocoaPods erhältlich .
Beispielcode (Swift)
import ScrollableStackView
var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)
// add your views with
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...
Beispielcode (Ziel-C)
@import ScrollableStackView
ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];
UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];
// add your views with
[scrollable.stackView addArrangedSubview:rectangle];
// ...
Wenn Sie die Stapelansicht vertikal in der Bildlaufansicht zentrieren müssen, entfernen Sie sie einfach.
Beispiel für eine vertikale Stapelansicht / Bildlaufansicht (mit der Option für das automatische Layout EasyPeasy
):
let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView <- [
Edges(),
Width().like(self.view)
]
let stackView = UIStackView(arrangedSubviews: yourSubviews)
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
scrollView.addSubview(stackView)
stackView <- [
Edges(),
Width().like(self.view)
]
Stellen Sie einfach sicher, dass jede Höhe Ihrer Unteransicht definiert ist!
Entwerfen Sie in erster Linie Ihre Ansicht, vorzugsweise in einer Art Skizze, oder machen Sie sich ein Bild davon, was Sie als scrollbaren Inhalt wünschen.
Machen Sie danach die freie Form des Ansichtscontrollers (wählen Sie aus dem Attributinspektor) und stellen Sie Höhe und Breite gemäß der inhaltlichen Größe Ihrer Ansicht ein (aus dem Größeninspektor auszuwählen).
Danach legen Sie im Ansichts-Controller eine Bildlaufansicht ab. Dies ist eine Logik, die in iOS fast immer funktioniert (möglicherweise muss die Dokumentation dieser Ansichtsklasse durchgesehen werden, die Sie über Befehl + Klicken erhalten können diese Klasse oder über googeln)
Wenn Sie mit zwei oder mehr Ansichten arbeiten, beginnen Sie zuerst mit einer Ansicht, die früher eingeführt wurde oder primitiver ist, und wechseln Sie dann zu der Ansicht, die später eingeführt wurde oder moderner ist. Beginnen Sie hier also, da zuerst die Bildlaufansicht eingeführt wurde, zuerst mit der Bildlaufansicht und wechseln Sie dann zur Stapelansicht. Setzen Sie hier die Einschränkungen für die Bildlaufansicht in alle Richtungen gegenüber der Superansicht auf Null. Fügen Sie alle Ihre Ansichten in diese Bildlaufansicht ein und fügen Sie sie dann in die Stapelansicht ein.
Während der Arbeit mit der Stapelansicht
Beginnen Sie zuerst mit Grounds Up (Bottom-Up-Ansatz), dh wenn Sie Beschriftungen, Textfelder und Bilder in Ihrer Ansicht haben, legen Sie diese Ansichten zuerst an (in der Bildlaufansicht) und fügen Sie sie anschließend in die Stapelansicht ein.
Danach optimieren Sie die Eigenschaft der Stapelansicht. Wenn die gewünschte Ansicht immer noch nicht erreicht wird, verwenden Sie eine andere Stapelansicht.
Fügen Sie nach dem Erstellen Ihrer Ansicht eine Einschränkung zwischen der Mutterstapelansicht und der Bildlaufansicht ein, während die untergeordnete Stapelansicht mit der Mutterstapelansicht eingeschränkt wird. Hoffentlich sollte es zu diesem Zeitpunkt gut funktionieren, oder Sie erhalten eine Warnung von Xcode mit Vorschlägen, lesen Sie die darin enthaltenen Informationen und implementieren Sie diese. Hoffentlich sollten Sie jetzt eine Arbeitssicht haben, die Ihren Erwartungen entspricht :).
Für die verschachtelte oder einzelne Stapelansicht muss die Bildlaufansicht mit der Stammansicht auf eine feste Breite eingestellt werden. Die Hauptstapelansicht innerhalb der Bildlaufansicht muss dieselbe Breite haben. [Meine Bildlaufansicht befindet sich unten in einer Ansicht. Ignorieren Sie sie.]
Richten Sie eine Einschränkung für die gleiche Breite zwischen UIStackView und UIScrollView ein.
Wenn jemand nach einer horizontalen Bildlaufansicht sucht
func createHorizontalStackViewsWithScroll() {
self.view.addSubview(stackScrollView)
stackScrollView.translatesAutoresizingMaskIntoConstraints = false
stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
stackScrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true
stackView.distribution = .equalSpacing
stackView.spacing = 5
stackView.axis = .horizontal
stackView.alignment = .fill
for i in 0 ..< images.count {
let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
// set button image
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true
stackView.addArrangedSubview(photoView)
}
stackView.setNeedsLayout()
}
Platzieren Sie eine Bildlaufansicht in Ihrer Szene und passen Sie sie so an, dass sie die Szene ausfüllt. Platzieren Sie dann eine Stapelansicht in der Bildlaufansicht und die Schaltfläche Element hinzufügen in der Stapelansicht. Sobald alles vorhanden ist, legen Sie die folgenden Einschränkungen fest:
Scroll View.Leading = Superview.LeadingMargin
Scroll View.Trailing = Superview.TrailingMargin
Scroll View.Top = Superview.TopMargin
Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
Stack View.Leading = Scroll View.Leading
Stack View.Trailing = Scroll View.Trailing
Stack View.Top = Scroll View.Top
Stack View.Bottom = Scroll View.Bottom
Stack View.Width = Scroll View.Width
Code: Stack View.Width = Scroll View.Width
ist der Schlüssel.
Hinzufügen einer neuen Perspektive für macOS Catalyst. Da MacOS-Apps die Größenänderung von Fenstern unterstützen, ist es möglich, dass Sie UIStackView
von einem nicht scrollbaren Status zu einem scrollbaren Status wechseln oder umgekehrt. Hier gibt es zwei subtile Dinge:
UIStackView
ist für alle Bereiche geeignet.UIScrollView
wird versucht, die Größe zu ändern, um den neu gewonnenen / verlorenen Bereich unter Ihrer Navigationsleiste (oder die Symbolleiste bei macOS-Apps) zu berücksichtigen.Dies wird leider eine Endlosschleife erzeugen. Ich bin nicht sehr vertraut mit UIScrollView
und seine adjustedContentInset
, aber aus meinem Protokoll in seiner layoutSubviews
Methode sehe ich das folgende Verhalten:
UIScrollView
versucht, seine Grenzen zu verkleinern (da der Bereich unter der Symbolleiste nicht benötigt wird).UIStackView
folgt.UIScrollView
ist er unzufrieden und beschließt, die größeren Grenzen wiederherzustellen. Das fühlt sich für mich sehr seltsam an, da ich das aus dem Protokoll sehe UIScrollView.bounds.height == UIStackView.bounds.height
.UIStackView
folgt.Es scheint mir, dass zwei Schritte das Problem beheben würden:
UIStackView.top
aufUIScrollView.topMargin
.contentInsetAdjustmentBehavior
zu .never
.Hier geht es um eine vertikal scrollbare Ansicht mit einer vertikal wachsenden UIStackView
. Ändern Sie für ein horizontales Paar den Code entsprechend.
Hoffe, es hilft jedem in der Zukunft. Ich konnte niemanden finden, der dies im Internet erwähnte, und es hat mich ziemlich lange gekostet, herauszufinden, was passiert ist.