Diciamo che ho aggiunto più viste in UIStackView che possono essere visualizzate, come posso fare lo UIStackView
scorrimento?
Diciamo che ho aggiunto più viste in UIStackView che possono essere visualizzate, come posso fare lo UIStackView
scorrimento?
Risposte:
Nel caso in cui qualcuno stia cercando una soluzione senza codice , ho creato un esempio per farlo completamente nello storyboard, usando Auto Layout.
Puoi ottenerlo da Github .
Fondamentalmente, per ricreare l'esempio (per lo scorrimento verticale):
UIScrollView
e impostarne i vincoli.UIStackView
aUIScrollView
Leading
, Trailing
, Top
e Bottom
dovrebbe essere pari a quelli diUIScrollView
Width
vincolo uguale tra il UIStackView
e UIScrollView
.UIStackView
UIViews
alUIStackView
Scambiare Width
per Height
nel passaggio 4 e impostare Axis
= Horizontal
nel passaggio 5 per ottenere un UIStackView orizzontale.
La Guida al layout automatico di Apple include un'intera sezione sull'utilizzo delle viste di scorrimento . Alcuni frammenti rilevanti:
- Appunta i bordi superiore, inferiore, iniziale e finale della vista contenuto ai bordi corrispondenti della vista di scorrimento. La vista del contenuto ora definisce l'area del contenuto della vista di scorrimento.
- (Facoltativo) Per disabilitare lo scorrimento orizzontale, impostare la larghezza della vista del contenuto uguale alla larghezza della vista di scorrimento. La vista contenuto ora riempie la vista di scorrimento in senso orizzontale.
- (Facoltativo) Per disabilitare lo scorrimento verticale, impostare l'altezza della vista del contenuto uguale all'altezza della vista di scorrimento. La vista contenuto ora riempie la vista di scorrimento in senso orizzontale.
Inoltre:
Il layout deve definire completamente la dimensione della vista del contenuto (tranne dove definito ai passaggi 5 e 6). ... Quando la vista del contenuto è più alta della vista di scorrimento, la vista di scorrimento consente lo scorrimento verticale. Quando la vista del contenuto è più ampia della vista di scorrimento, la vista di scorrimento consente lo scorrimento orizzontale.
Per riassumere, la vista del contenuto della vista di scorrimento (in questo caso, una vista di pila) deve essere appuntata ai suoi bordi e avere la sua larghezza e / o altezza altrimenti vincolate. Ciò significa che il contenuto della vista pila deve essere vincolato (direttamente o indirettamente) nella direzione o nelle direzioni in cui si desidera lo scorrimento, il che potrebbe significare aggiungere un vincolo di altezza a ciascuna vista all'interno di una vista pila a scorrimento verticale, ad esempio. Di seguito è riportato un esempio di come consentire lo scorrimento verticale di una vista di scorrimento contenente una vista pila:
// 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.
questo errore scompare solo se si imposta un vincolo di larghezza e altezza per la vista pila, che disabilita lo scorrimento verticale. Questo codice funziona ancora per te?
I vincoli nella risposta più votata qui hanno funzionato per me e ho incollato un'immagine dei vincoli di seguito, come creato nel mio storyboard.
Ho riscontrato due problemi, ma altri dovrebbero essere a conoscenza:
Dopo aver aggiunto vincoli simili a quelli presenti nella risposta accettata, viene visualizzato l'errore di autolayout rosso Need constraints for: X position or width
. Ciò è stato risolto aggiungendo un UILabel come sottoview della vista dello stack.
Sto aggiungendo le visualizzazioni secondarie a livello di codice, quindi inizialmente non avevo visualizzazioni secondarie nello storyboard. Per eliminare gli errori di autolayout, aggiungi una sottoview allo storyboard, quindi rimuovila al caricamento prima di aggiungere le tue sottoview e i tuoi vincoli reali.
Inizialmente ho tentato di aggiungere UIButtons
a UIStackView. I pulsanti e le viste verranno caricati, ma la vista di scorrimento non scorrerà . Ciò è stato risolto aggiungendo UILabels
alla vista pila invece dei pulsanti. Usando gli stessi vincoli, questa gerarchia di viste con UILabels scorre ma gli UIButton no.
Sono confuso da questo problema, in quanto i UIButtons non sembrano avere un IntrinsicContentSize (utilizzato dal Stack View). Se qualcuno sa perché i pulsanti non funzionano, mi piacerebbe sapere perché.
Ecco la gerarchia e i vincoli della mia vista, per riferimento:
Come dice Eik, UIStackView
e UIScrollView
suona bene insieme, vedi qui .
La chiave è che UIStackView
gestisce l'altezza / larghezza variabile per diversi contenuti e UIScrollView
quindi fa bene il suo compito di scorrere / rimbalzare quel contenuto:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
Stavo cercando di fare la stessa cosa e mi sono imbattuto in questo eccellente post. Se vuoi farlo a livello di codice usando l'API anchor, questa è la strada da percorrere.
Riassumendo, incorpora il tuo UIStackView
nel tuo UIScrollView
e imposta i vincoli di ancoraggio di UIStackView
in modo che corrispondano a quelli di 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
Avere una scena vuota a schermo intero
Aggiungi una vista di scorrimento. Control-trascina dalla vista di scorrimento alla vista di base , aggiungi sinistra-destra-alto-basso, tutto zero.
Aggiungi una vista pila nella vista di scorrimento. Control-trascina dalla vista dello stack alla vista di scorrimento , aggiungi sinistra-destra-alto-basso, tutto zero.
Inserisci due o tre etichette nella vista pila.
Per chiarezza, rendere rosso il colore di sfondo dell'etichetta. Impostare l'altezza dell'etichetta su 100.
Ora imposta la larghezza di ogni UILabel:
Sorprendentemente, trascina il controllo da UILabel
alla vista di scorrimento, non alla vista di pila, e seleziona larghezze uguali .
Ripetere:
È così semplice. Questo è il segreto
Suggerimento segreto - Bug Apple:
Non funzionerà con un solo oggetto! Aggiungi alcune etichette per far funzionare la demo.
Hai finito.
Suggerimento: è necessario aggiungere un'altezza a ogni nuovo elemento . Ogni elemento in qualsiasi vista a pila scorrevole deve avere una dimensione intrinseca (come un'etichetta) o aggiungere un vincolo di altezza esplicito.
L'approccio alternativo:
Quanto sopra: sorprendentemente, imposta le larghezze di UILabels sulla larghezza della vista di scorrimento (non della vista di pila).
In alternativa ...
Quindi hai due opzioni:
o
Per essere chiari, esegui UNO di questi metodi, NON fare entrambi.
Per lo scorrimento orizzontale. Innanzitutto, crea a UIStackView
e a UIScrollView
e aggiungili alla tua vista nel modo seguente:
let scrollView = UIScrollView()
let stackView = UIStackView()
scrollView.addSubview(stackView)
view.addSubview(scrollView)
Ricordando di impostare translatesAutoresizingMaskIntoConstraints
su false
su UIStackView
e il UIScrollView
:
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
Per far funzionare tutto l'ancoraggio finale, anteriore, superiore e inferiore UIStackView
devono essere uguali a quelli UIScrollView
:
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
Ma l'ancora di larghezza del UIStackView
mosto è uguale o maggiore della larghezza UIScrollView
dell'ancora:
stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true
Ora ancorate il vostro UIScrollView
, ad esempio:
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
Quindi, suggerirei di provare le seguenti impostazioni per l' UIStackView
allineamento e la distribuzione:
topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center
topicStackView.spacing = 10
Infine, dovrai utilizzare il addArrangedSubview:
metodo per aggiungere visualizzazioni secondarie a UIStackView.
Un'ulteriore caratteristica che potresti trovare utile è che, poiché UIStackView
è racchiuso in un, UIScrollView
hai ora accesso agli inserti di testo per rendere le cose un po 'più carine.
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
Aggiungi questo a viewdidload
:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
Puoi provare ScrollableStackView : https://github.com/gurhub/ScrollableStackView
È una libreria compatibile con Objective-C e Swift. È disponibile tramite CocoaPods .
Codice di esempio (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)
// ...
Codice di esempio (Objective-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];
// ...
Se hai un vincolo per centrare verticalmente la vista pila all'interno della vista di scorrimento, rimuovila.
Esempio per uno stackview / scrollview verticale (usando il EasyPeasy
per autolayout):
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)
]
Assicurati solo che ogni altezza del tuo subview sia definita!
Prima di tutto, progetta la tua vista, preferibilmente in qualcosa come Sketch o fatti un'idea di cosa desideri come contenuto scorrevole.
Dopodiché, rendi il modulo di controllo della vista libero (scegli dalla finestra di ispezione degli attributi) e imposta l'altezza e la larghezza secondo le dimensioni del contenuto intrinseco della tua vista (da scegliere dalla finestra di ispezione delle dimensioni).
Dopodiché nel view controller inserisci una vista di scorrimento e questa è una logica, che ho scoperto funzionare quasi tutte le volte in iOS (potrebbe essere necessario consultare la documentazione di quella classe di visualizzazione che puoi ottenere tramite comando + clic su quella classe o tramite google)
Se stai lavorando con due o più viste, inizia innanzitutto con una vista, che è stata introdotta in precedenza o è più primitiva, quindi vai alla vista che è stata introdotta in seguito o è più moderna. Quindi, poiché la vista di scorrimento è stata introdotta per prima, inizia prima con la vista di scorrimento e poi vai alla vista di pila. Qui metti i vincoli della vista di scorrimento a zero in tutte le direzioni rispetto alla sua super vista. Inserisci tutte le tue viste all'interno di questa vista di scorrimento e quindi inseriscile in vista pila.
Mentre si lavora con la vista pila
Prima di tutto inizia con il ground up (approccio bottom-up), cioè se hai etichette, campi di testo e immagini nella tua vista, quindi prima disponi queste viste (all'interno della vista di scorrimento) e poi mettile nella vista pila.
Successivamente, modifica la proprietà della vista stack. Se la vista desiderata non viene ancora raggiunta, utilizzare un'altra vista pila.
Dopo aver creato la vista, inserire un vincolo tra la vista pila madre e la vista di scorrimento, mentre la vista pila figlio vincola con la vista pila madre. Spero che a questo punto dovrebbe funzionare bene o potresti ricevere un avviso da Xcode che ti dà suggerimenti, leggi cosa dice e implementa quelli. Spero che ora dovresti avere una visione operativa secondo le tue aspettative :).
Per la vista Stack nidificata o singola, la vista di scorrimento deve essere impostata su una larghezza fissa con la vista radice. La vista pila principale che si trova all'interno della vista di scorrimento deve impostare la stessa larghezza. [La mia vista di scorrimento è sotto una vista ignorala]
Impostare un vincolo Larghezza uguale tra UIStackView e UIScrollView.
Se qualcuno cerca una vista di scorrimento orizzontale
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()
}
Posiziona una vista di scorrimento sulla scena e ridimensionala in modo che riempia la scena. Quindi, posizionare una vista pila all'interno della vista scorrimento e posizionare il pulsante Aggiungi elemento all'interno della vista pila. Non appena tutto è a posto, imposta i seguenti vincoli:
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
codice: Stack View.Width = Scroll View.Width
è la chiave.
Aggiunta di alcune nuove prospettive per macOS Catalyst. Poiché le app macOS supportano il ridimensionamento delle finestre, è possibile che UIStackView
passerai da uno stato non scrutabile a uno scorrevole, o viceversa. Ci sono due cose sottili qui:
UIStackView
è progettato per adattarsi a tutta l'area che può.UIScrollView
tenterà di ridimensionare i suoi limiti per tenere conto dell'area appena guadagnata / persa sotto la barra di navigazione (o barra degli strumenti nel caso delle app macOS).Questo purtroppo creerà un ciclo infinito. Non ho molta familiarità con UIScrollView
e il suo adjustedContentInset
, ma dal mio log nel suo layoutSubviews
metodo, sto vedendo il seguente comportamento:
UIScrollView
tenta di ridurne i limiti (poiché non è necessario per l'area sotto la barra degli strumenti).UIStackView
segue.UIScrollView
non è soddisfatto e decide di ripristinare i limiti più grandi. Mi sembra molto strano dato che quello che vedo dal registro è quello UIScrollView.bounds.height == UIStackView.bounds.height
.UIStackView
segue.Mi sembra che due passaggi risolverebbero il problema:
UIStackView.top
a UIScrollView.topMargin
.contentInsetAdjustmentBehavior
su .never
.Qui mi occupo di una vista scorrevole verticalmente con una crescita verticale UIStackView
. Per una coppia orizzontale, modificare il codice di conseguenza.
Spero che possa aiutare chiunque in futuro. Non sono riuscito a trovare nessuno che lo menzionasse su Internet e mi è costato molto tempo capire cosa è successo.