Come modificare il colore di sfondo di UIStackView?


159

Ho provato a cambiare lo UIStackViewsfondo da chiaro a bianco nella finestra di ispezione Storyboard, ma durante la simulazione il colore di sfondo della vista pila ha ancora un colore chiaro.
Come posso cambiare il colore di sfondo di un UIStackView?


2
questo è uno di quei rari casi su SO, dove le risposte sono semplicemente totalmente sbagliate . basta aggiungere una ... vista di sfondo. è molto semplice. articolo di esempio che lo spiega: useyourloaf.com/blog/stack-view-background-color
Fattie

@Fattie Ha funzionato! Puoi aggiungerlo anche come risposta?
absin

ciao @AbSin - le risposte di kurrodu e MariánČerný sono perfette.
Fattie,

Haha, questo è fantastico, l'elemento non rendering ha una proprietà di colore di sfondo
Brad Thomas

Risposte:


289

Non puoi farlo: UIStackViewè una vista senza disegno, il che significa che drawRect()non viene mai chiamato e il suo colore di sfondo viene ignorato. Se desideri disperatamente un colore di sfondo, considera di posizionare la vista pila all'interno di un'altra UIViewe dare a quella vista un colore di sfondo.

Riferimento da QUI .

MODIFICARE:

Puoi aggiungere un subView a UIStackViewcome menzionato QUI o in questa risposta (sotto) e assegnargli un colore. Dai un'occhiata qui sotto extensionper quello:

extension UIStackView {
    func addBackground(color: UIColor) {
        let subView = UIView(frame: bounds)
        subView.backgroundColor = color
        subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        insertSubview(subView, at: 0)
    }
}

E puoi usarlo come:

stackView.addBackground(color: .red)

12
questo è abbastanza sbagliato . è un caso raro in cui l'articolo di Paul su HackingWithSwift è totalmente errato . aggiungi semplicemente un'altra vista (che non è una delle viste organizzate ) e questa è la risposta.
Fattie,

11
ecco un articolo che lo spiega, è banale: useyourloaf.com/blog/stack-view-background-color
Fattie

1
Quindi, a che cosa serve stackview, non possiamo semplicemente aggiungere le nostre viste all'interno di un'altra vista e dargli dei vincoli per allinearlo in orizzontale o in verticale e in questo modo evitare totalmente stackview. Non è ridicolo avere viste all'interno di stackview e quella vista all'interno di UIView e quindi aggiungere questa UIView nel nostro componente.
Shivam Pokhriyal,

Almeno, drawRect()si chiama. Puoi semplicemente provare UIStackView
eseguendo la

Bella risposta. L'ho ampliato un po ', perché ho diversi temi nella mia app in modo che il colore di sfondo possa cambiare. Per impedire l'aggiunta di una sottoview ogni volta che il colore cambia, cambio il tag della sottoview a 123, quindi ogni volta che la funzione viene chiamata per prima cosa controllo se una delle subview ha il tag 123 come questo if let backgroundView = self.subviews.first(where: {$0.tag == 123}) {. In tal caso, cambio il colore di sfondo di quella vista.
guido,

47

Lo faccio così:

@IBDesignable
class StackView: UIStackView {
   @IBInspectable private var color: UIColor?
    override var backgroundColor: UIColor? {
        get { return color }
        set {
            color = newValue
            self.setNeedsLayout() // EDIT 2017-02-03 thank you @BruceLiu
        }
    }

    private lazy var backgroundLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        self.layer.insertSublayer(layer, at: 0)
        return layer
    }()
    override func layoutSubviews() {
        super.layoutSubviews()
        backgroundLayer.path = UIBezierPath(rect: self.bounds).cgPath
        backgroundLayer.fillColor = self.backgroundColor?.cgColor
    }
}

Funziona come un fascino


Questo non funziona per me quando utilizzato in uno storyboard, inclusa la risposta aggiornata con i metodi get / set di backgroundColor. : - / (iOS 10.2.1, Xcode 8.2.1, su iPad)
webjprgm

Mi scuso, l'ho capito. Interface Builder ha escluso il campo "modulo" sotto la classe, quindi non lo ha caricato. Risolvilo, quindi imposta lo sfondo nella vista del controller viewDidLoad risolto.
webjprgm,

1
rendi anche la tua classe IBDesignable -> @IBDesignable class StackViewche ti consentirà di progettare sulla scheda Story. Non mi interessa molto aggiungere uno sfondo in fase di esecuzione, ma è molto difficile progettare lo stackview quando non ha colore sullo
storyboard

@Fattie Care per spiegare perché è un cattivo approccio?
Arbitur,

forse Very Bad è un po 'duro @Arbitur :) :) ma non c'è bisogno di giocare con i livelli, dato che aggiungi solo un'altra vista (ma non quella
concordata

34

UIStackViewè un elemento non rendering e, come tale, non viene disegnato sullo schermo. Ciò significa che il cambiamento backgroundColoressenzialmente non fa nulla. Se vuoi cambiare il colore di sfondo, aggiungi semplicemente UIViewa come sottoview (che non è organizzato) come sotto:

extension UIStackView {

    func addBackground(color: UIColor) {
        let subview = UIView(frame: bounds)
        subview.backgroundColor = color
        subview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        insertSubview(subview, at: 0)
    }

}

1
anche questa risposta è perfetta. personalmente ho inserito tutti e quattro i vincoli (come nella risposta di kurrodu)
Fattie,

1
Supponendo che non cambi il colore di sfondo, va bene. Ma se si chiama questo metodo più volte le visualizzazioni delle volte precedenti rimangono comunque. Inoltre, non è possibile rimuovere facilmente il colore di sfondo con questo metodo.
keji,

11

Forse il modo più semplice, più leggibile e meno confuso sarebbe quello di incorporare UIStackViewin a UIViewe impostare il colore di sfondo alla vista.

E non dimenticare di configurare correttamente i vincoli di layout automatico tra queste due viste ... ;-)


2
Sì, ma dov'è il divertimento? :-)
webjprgm,

1
La migliore soluzione di gran lunga - funziona solo con Interface builder, senza una riga di codice
Vladimir Grigorov,

10

Pitiphong ha ragione, per ottenere uno stackview con un colore di sfondo fai qualcosa come il seguente ...

  let bg = UIView(frame: stackView.bounds)
  bg.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  bg.backgroundColor = UIColor.red

  stackView.insertSubview(bg, at: 0)

Questo ti darà uno stackview i cui contenuti saranno posizionati su uno sfondo rosso.

Per aggiungere il riempimento allo stackview in modo che i contenuti non siano allineati con i bordi, aggiungi quanto segue nel codice o nello storyboard ...

  stackView.isLayoutMarginsRelativeArrangement = true
  stackView.layoutMargins = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)

2
Avevo bisogno di usare stackView.insertSubview (bg, sottoSubview: stackView.arrangedSubviews [0]) Altrimenti la vista colorata veniva posizionata in cima allo stack.
iCyberPaul

questa risposta è quasi corretta, ma sbagliata : basta usare insert a zero.
Fattie,

1
@iCyberPaul: basta usare insert a zero.
Fattie

Nota il codice di esempio modificato per inserire a 0 in modo tale che la vista sia l'ultima nella gerarchia della vista. (Ho detto di fare qualcosa di simile al seguente ...)
Michael Long,

8

TL; DR: il modo ufficiale per farlo è quello di aggiungere una vista vuota nella vista pila usando addSubview: metodo e impostare invece lo sfondo della vista aggiunta.

La spiegazione: UIStackView è una sottoclasse UIView speciale che non disegna solo il layout. Quindi molte delle sue proprietà non funzioneranno come al solito. E poiché UIStackView impaginerà solo le sue visualizzazioni secondarie organizzate, ciò significa che puoi semplicemente aggiungerlo a UIView con il addSubview:metodo, impostarne i vincoli e il colore di sfondo. Questo è il modo ufficiale per ottenere ciò che desideri citato dalla sessione del WWDC


3

Questo funziona per me in Swift 3 e iOS 10:

let stackView = UIStackView()
let subView = UIView()
subView.backgroundColor = .red
subView.translatesAutoresizingMaskIntoConstraints = false
stackView.addSubview(subView) // Important: addSubview() not addArrangedSubview()

// use whatever constraint method you like to 
// constrain subView to the size of stackView.
subView.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
subView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
subView.leftAnchor.constraint(equalTo: stackView.leftAnchor).isActive = true
subView.rightAnchor.constraint(equalTo: stackView.rightAnchor).isActive = true

// now add your arranged subViews...
stackView.addArrangedSubview(button1)
stackView.addArrangedSubview(button2)

3

In iOS10, la risposta di @ Arbitur richiede un setNeedsLayout dopo aver impostato il colore. Questa è la modifica necessaria:

override var backgroundColor: UIColor? {
    get { return color }
    set { 
        color = newValue
        setNeedsLayout()
    }
}

Dipende da come si impostano le cose, se si imposta backgroundColorprima di aggiungerlo a superviewfunziona su ios10 e ios9, tuttavia, se si aggiorna il backgroundColordopo che la sua layoutSubviews()funzione è stata chiamata, semplicemente non aggiornerebbe backgroundColoril backgroundLayersignificato senza aggiornamento visivo. Risolto ora, grazie per averlo sottolineato.
Arbitur

2

Ecco una breve panoramica per l'aggiunta di un colore di sfondo della vista Stack.

class RevealViewController: UIViewController {

    @IBOutlet private weak var rootStackView: UIStackView!

Creazione di una vista di sfondo con angoli arrotondati

private lazy var backgroundView: UIView = {
    let view = UIView()
    view.backgroundColor = .purple
    view.layer.cornerRadius = 10.0
    return view
}()

Per farlo apparire come sfondo, lo aggiungiamo all'array delle visualizzazioni secondarie della vista dello stack radice all'indice 0. Ciò lo mette dietro le viste organizzate della vista dello stack.

private func pinBackground(_ view: UIView, to stackView: UIStackView) {
    view.translatesAutoresizingMaskIntoConstraints = false
    stackView.insertSubview(view, at: 0)
    view.pin(to: stackView)
}

Aggiungi vincoli per bloccare la vista di sfondo ai bordi della vista pila, usando una piccola estensione su UIView.

public extension UIView {
  public func pin(to view: UIView) {
    NSLayoutConstraint.activate([
      leadingAnchor.constraint(equalTo: view.leadingAnchor),
      trailingAnchor.constraint(equalTo: view.trailingAnchor),
      topAnchor.constraint(equalTo: view.topAnchor),
      bottomAnchor.constraint(equalTo: view.bottomAnchor)
      ])
  }
}

chiama il pinBackgrounddaviewDidLoad

override func viewDidLoad() {
  super.viewDidLoad()
  pinBackground(backgroundView, to: rootStackView)
}

Riferimento da: QUI


2

Potresti fare una piccola estensione di UIStackView

extension UIStackView {
    func setBackgroundColor(_ color: UIColor) {
        let backgroundView = UIView(frame: .zero)
        backgroundView.backgroundColor = color
        backgroundView.translatesAutoresizingMaskIntoConstraints = false
        self.insertSubview(backgroundView, at: 0)
        NSLayoutConstraint.activate([
            backgroundView.topAnchor.constraint(equalTo: self.topAnchor),
            backgroundView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            backgroundView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            backgroundView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
            ])
    }
}

Uso:

yourStackView.setBackgroundColor(.black)

1

Xamarin, versione C #:

var stackView = new UIStackView { Axis = UILayoutConstraintAxis.Vertical };

UIView bg = new UIView(stackView.Bounds);
bg.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
bg.BackgroundColor = UIColor.White;
stackView.AddSubview(bg);

0
UIStackView *stackView;
UIView *stackBkg = [[UIView alloc] initWithFrame:CGRectZero];
stackBkg.backgroundColor = [UIColor redColor];
[self.view insertSubview:stackBkg belowSubview:stackView];
stackBkg.translatesAutoresizingMaskIntoConstraints = NO;
[[stackBkg.topAnchor constraintEqualToAnchor:stackView.topAnchor] setActive:YES];
[[stackBkg.bottomAnchor constraintEqualToAnchor:stackView.bottomAnchor] setActive:YES];
[[stackBkg.leftAnchor constraintEqualToAnchor:stackView.leftAnchor] setActive:YES];
[[stackBkg.rightAnchor constraintEqualToAnchor:stackView.rightAnchor] setActive:YES];

0

Sottoclasse UIStackView

class CustomStackView : UIStackView {

private var _bkgColor: UIColor?
override public var backgroundColor: UIColor? {
    get { return _bkgColor }
    set {
        _bkgColor = newValue
        setNeedsLayout()
    }
}

private lazy var backgroundLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    self.layer.insertSublayer(layer, at: 0)
    return layer
}()

override public func layoutSubviews() {
    super.layoutSubviews()
    backgroundLayer.path = UIBezierPath(rect: self.bounds).cgPath
    backgroundLayer.fillColor = self.backgroundColor?.cgColor
}
}

Quindi nella tua classe

yourStackView.backgroundColor = UIColor.lightGray

0

Puoi inserire un sublayer in StackView, funziona per me:

@interface StackView ()
@property (nonatomic, strong, nonnull) CALayer *ly;
@end

@implementation StackView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        _ly = [CALayer new];
        [self.layer addSublayer:_ly];
    }
    return self;
}

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    [super setBackgroundColor:backgroundColor];
    self.ly.backgroundColor = backgroundColor.CGColor;
}

- (void)layoutSubviews {
    self.ly.frame = self.bounds;
    [super layoutSubviews];
}

@end

0

Sono un po 'scettico nei componenti della sottoclasse dell'interfaccia utente. Ecco come lo sto usando,

struct CustomAttributeNames{
        static var _backgroundView = "_backgroundView"
    }

extension UIStackView{

var backgroundView:UIView {
        get {
            if let view = objc_getAssociatedObject(self, &CustomAttributeNames._backgroundView) as? UIView {
                return view
            }
            //Create and add
            let view = UIView(frame: .zero)
            view.translatesAutoresizingMaskIntoConstraints = false
            insertSubview(view, at: 0)
            NSLayoutConstraint.activate([
              view.topAnchor.constraint(equalTo: self.topAnchor),
              view.leadingAnchor.constraint(equalTo: self.leadingAnchor),
              view.bottomAnchor.constraint(equalTo: self.bottomAnchor),
              view.trailingAnchor.constraint(equalTo: self.trailingAnchor)
            ])

            objc_setAssociatedObject(self,
                                     &CustomAttributeNames._backgroundView,
                                     view,
                                     objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)

            return view
        }
    }
}

E questo è l'uso,

stackView.backgroundView.backgroundColor = .white
stackView.backgroundView.layer.borderWidth = 2.0
stackView.backgroundView.layer.borderColor = UIColor.red.cgColor
stackView.backgroundView.layer.cornerRadius = 4.0

Nota: con questo approccio, se si desidera impostare il bordo, è necessario impostare layoutMarginssu StackView in modo che il bordo sia visibile.


0

Non è possibile aggiungere sfondo a Stackview. Ma quello che puoi fare è aggiungere stackview in una vista e quindi impostare lo sfondo della vista per fare il lavoro. * Non interromperà i flussi di stackview. Spero che questo possa aiutare.


0

Possiamo avere una classe personalizzata StackViewcome questa:

class StackView: UIStackView {
    lazy var backgroundView: UIView = {
        let otherView = UIView()
        addPinedSubview(otherView)
        return otherView
    }()
}

extension UIView {
    func addPinedSubview(_ otherView: UIView) {
        addSubview(otherView)
        otherView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            otherView.trailingAnchor.constraint(equalTo: trailingAnchor),
            otherView.topAnchor.constraint(equalTo: topAnchor),
            otherView.heightAnchor.constraint(equalTo: heightAnchor),
            otherView.widthAnchor.constraint(equalTo: widthAnchor),
        ])
    }
}

E può essere usato in questo modo:

let stackView = StackView()
stackView.backgroundView.backgroundColor = UIColor.lightGray

È leggermente meglio che aggiungere una funzione di estensione func addBackground(color: UIColor)come suggerito da altri. La vista di sfondo è pigra in modo che non verrà creata fino a quando non si chiama effettivamente stackView.backgroundView.backgroundColor = .... Inoltre, l'impostazione / modifica del colore di sfondo più volte non comporterà l'inserimento di più visualizzazioni secondarie nella visualizzazione in pila.


0

Se si desidera controllare dal designer stesso, aggiungere questa estensione alla vista pila

 @IBInspectable var customBackgroundColor: UIColor?{
     get{
        return backgroundColor
     }
     set{
        backgroundColor = newValue
         let subview = UIView(frame: bounds)
         subview.backgroundColor = newValue
         subview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
         insertSubview(subview, at: 0)
     }
 }

-1

Potresti farlo in questo modo:

stackView.backgroundColor = UIColor.blue

Fornendo un'estensione per sostituire backgroundColor:

extension UIStackView {

    override open var backgroundColor: UIColor? {

        get {
            return super.backgroundColor
        }

        set {

            super.backgroundColor = newValue

            let tag = -9999
            for view in subviews where view.tag == tag {
                view.removeFromSuperview()
            }

            let subView = UIView()
            subView.tag = tag
            subView.backgroundColor = newValue
            subView.translatesAutoresizingMaskIntoConstraints = false
            self.addSubview(subView)
            subView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
            subView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
            subView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
            subView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
        }

    }

}

devi impostare la posizione corretta della sottoview, usa insert at
Fattie

-1

Prova BoxView . È simile a UIStackView, ma è il rendering e supporta più opzioni di layout.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.