Wie ändere ich die Hintergrundfarbe von UIStackView?


157

Ich habe versucht, den UIStackViewHintergrund im Storyboard-Inspektor von klar auf weiß zu ändern , aber beim Simulieren hat die Hintergrundfarbe der Stapelansicht immer noch eine klare Farbe.
Wie kann ich die Hintergrundfarbe von a ändern UIStackView?


2
Dies ist einer der seltenen Fälle bei SO, in denen die Antworten einfach völlig falsch sind . Sie fügen einfach eine ... Hintergrundansicht hinzu. es ist sehr einfach. Beispiel Artikel, der es erklärt: useyourloaf.com/blog/stack-view-background-color
Fattie

@ Fattie Es hat funktioniert! Können Sie es auch als Antwort hinzufügen?
Absin

hi @AbSin - die Antworten von kurrodu und MariánČerný sind perfekt.
Fattie

Haha, das ist großartig, nicht renderendes Element hat eine Hintergrundfarben-Eigenschaft
Brad Thomas

Antworten:


289

Sie können dies nicht tun - UIStackViewist eine Ansicht ohne Zeichnung, dh sie drawRect()wird nie aufgerufen und ihre Hintergrundfarbe wird ignoriert. Wenn Sie unbedingt eine Hintergrundfarbe wünschen, sollten Sie die Stapelansicht in einer anderen platzieren UIViewund dieser Ansicht eine Hintergrundfarbe geben.

Referenz von HIER .

BEARBEITEN:

Sie können eine Unteransicht UIStackViewwie HIER oder in dieser Antwort (unten) hinzufügen und dieser eine Farbe zuweisen. Schauen Sie sich das unten extensionan:

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

Und Sie können es verwenden wie:

stackView.addBackground(color: .red)

12
das ist ganz falsch . Es ist ein seltener Fall, in dem Pauls Artikel über HackingWithSwift völlig falsch ist . Sie fügen einfach eine andere Ansicht hinzu (die nicht zu den angeordneten Ansichten gehört), und das ist die Antwort.
Fattie

11
Hier ist ein Artikel, der es erklärt, es ist trivial: useyourloaf.com/blog/stack-view-background-color
Fattie

1
Was ist dann die Verwendung von Stackview? Können wir unsere Ansichten nicht einfach in eine andere Ansicht einfügen und ihr Einschränkungen geben, um sie entweder horizontal oder vertikal auszurichten, und so die Stackview vollständig vermeiden. Ist es nicht lächerlich, Ansichten in Stackview und diese Stackview in UIView zu haben und diese UIView dann in unsere Komponente aufzunehmen?
Shivam Pokhriyal

Zumindest drawRect()heißt. Sie können einfach durch Unterklasse testenUIStackView
khcpietro

Gute Antwort. Ich habe es ein wenig erweitert, da meine App mehrere Themen enthält, sodass sich die Hintergrundfarbe möglicherweise ändert. Um zu verhindern, dass bei jeder Farbänderung eine Unteransicht hinzugefügt wird, ändere ich das Tag der Unteransicht auf 123. Bei jedem Aufruf der Funktion überprüfe ich zunächst, ob eine der Unteransichten das Tag 123 wie dieses hat if let backgroundView = self.subviews.first(where: {$0.tag == 123}) {. Wenn ja, ändere ich die Hintergrundfarbe dieser Ansicht.
Guido

47

So mach ich es:

@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
    }
}

Klappt wunderbar


Dies funktioniert bei Verwendung in einem Storyboard nicht, einschließlich der aktualisierten Antwort mit den get / set-Methoden von backgroundColor. : - / (iOS 10.2.1, Xcode 8.2.1, auf dem iPad)
webjprgm

Entschuldigung, ich habe es herausgefunden. Interface Builder hat das Feld "Modul" unter der Klasse weggelassen und es nicht geladen. Korrigieren Sie das und setzen Sie den Hintergrund in viewDidLoad des View Controllers.
Webjprgm

1
Machen Sie auch Ihre Klasse IBDesignable -> @IBDesignable class StackViewDamit können Sie auf Storyboard entwerfen. Ich mag es nicht besonders, zur Laufzeit einen Hintergrund hinzuzufügen, aber es ist sehr schwierig, die Stapelansicht zu entwerfen, wenn sie keine Farbe auf dem Storyboard hat
FlowUI. SimpleUITesting.com

@Fattie Care zu erklären, warum es ein schlechter Ansatz ist?
Arbitur

vielleicht ist sehr schlecht ein bisschen hart @Arbitur :) :) aber es besteht keine Notwendigkeit, mit den Ebenen zu spielen, da Sie nur eine andere Ansicht hinzufügen (aber keine arrangierte)
Fattie

34

UIStackViewist ein nicht renderendes Element und wird daher nicht auf dem Bildschirm gezeichnet. Dies bedeutet, dass Änderungen im backgroundColorWesentlichen nichts bewirken. Wenn Sie die Hintergrundfarbe ändern möchten, fügen UIViewSie einfach eine Unteransicht (die nicht angeordnet ist) wie folgt hinzu:

extension UIStackView {

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

}

1
Diese Antwort ist auch perfekt. persönlich habe ich alle vier Einschränkungen eingegeben (wie in der Antwort von kurrodu)
Fattie

1
Vorausgesetzt, Sie ändern die Hintergrundfarbe nicht, ist dies in Ordnung. Wenn Sie diese Methode jedoch mehrmals aufrufen, bleiben die Ansichten aus den vorherigen Zeiten weiterhin erhalten. Es gibt auch keine Möglichkeit, die Hintergrundfarbe mit dieser Methode einfach zu entfernen.
Keji

11

Vielleicht wäre der einfachste, lesbarere und weniger hackige Weg, das UIStackViewin ein einzubetten UIViewund die Hintergrundfarbe auf die Ansicht einzustellen.

Und vergessen Sie nicht, die Einschränkungen für das automatische Layout zwischen diesen beiden Ansichten richtig zu konfigurieren… ;-)


2
Ja, aber wo ist der Spaß dabei? :-)
webjprgm

1
Die mit Abstand beste Lösung - sie funktioniert allein im Interface Builder ohne Codezeile
Vladimir Grigorov

10

Pitiphong ist richtig, um eine Stapelansicht mit einer Hintergrundfarbe zu erhalten, gehen Sie wie folgt vor ...

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

  stackView.insertSubview(bg, at: 0)

Dadurch erhalten Sie eine Stapelansicht, deren Inhalt auf einem roten Hintergrund platziert wird.

Um der Stapelansicht eine Auffüllung hinzuzufügen, damit der Inhalt nicht bündig mit den Kanten abschließt, fügen Sie Folgendes im Code oder im Storyboard hinzu ...

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

2
Ich musste stackView.insertSubview verwenden (bg, underSubview: stackView.arrangedSubviews [0]). Andernfalls wurde die farbige Ansicht oben auf dem Stapel platziert.
iCyberPaul

Diese Antwort ist fast richtig, aber falsch - Sie verwenden nur Einfügen bei Null.
Fattie

1
@iCyberPaul - Sie verwenden einfach Einfügen bei Null.
Fattie

Beachten Sie den Beispielcode, der so bearbeitet wurde, dass er bei 0 eingefügt wird, sodass die Ansicht die letzte in der Ansichtshierarchie ist. (Ich habe gesagt, machen Sie so etwas wie das Folgende ...)
Michael Long

8

TL; DR: Der offizielle Weg, dies zu tun, besteht darin, eine leere Ansicht mithilfe der addSubview:Methode in die Stapelansicht einzufügen und stattdessen den Hintergrund der hinzugefügten Ansicht festzulegen.

Die Erklärung: UIStackView ist eine spezielle UIView-Unterklasse, die nur das Layout und nicht das Zeichnen ausführt. So viele seiner Eigenschaften funktionieren nicht wie gewohnt. Und da UIStackView nur die angeordneten Unteransichten anordnet, können Sie ihm einfach eine UIView mit addSubview:Methode hinzufügen , die Einschränkungen und die Hintergrundfarbe festlegen. Dies ist der offizielle Weg, um das zu erreichen, was Sie aus der WWDC-Sitzung zitieren möchten


3

Dies funktioniert bei mir in Swift 3 und 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 benötigt die Antwort von @ Arbitur ein setNeedsLayout, nachdem die Farbe festgelegt wurde. Dies ist die Änderung, die benötigt wird:

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

Hängt davon ab, wie Sie die Dinge einrichten. Wenn Sie das festlegen, backgroundColorbevor Sie es zu einem hinzufügen, superviewfunktioniert es auf ios10 und ios9. Wenn Sie jedoch das aktualisiert haben, backgroundColornachdem seine layoutSubviews()Funktion aufgerufen wurde, wird backgroundColordie backgroundLayerBedeutung einfach kein visuelles Update aktualisiert. Jetzt behoben, danke für den Hinweis.
Arbitur

2

Hier ist eine kurze Übersicht zum Hinzufügen einer Hintergrundfarbe für die Stapelansicht.

class RevealViewController: UIViewController {

    @IBOutlet private weak var rootStackView: UIStackView!

Hintergrundansicht mit abgerundeten Ecken erstellen

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

Damit es als Hintergrund angezeigt wird, fügen wir es dem Unteransichtsarray der Stammstapelansicht bei Index 0 hinzu. Dadurch wird es hinter die angeordneten Ansichten der Stapelansicht verschoben.

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

Fügen Sie mithilfe einer kleinen Erweiterung in UIView Einschränkungen hinzu, um die Hintergrundansicht an den Rändern der Stapelansicht anzuheften.

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)
      ])
  }
}

Rufen Sie die pinBackgroundvonviewDidLoad

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

Referenz von: HIER


2

Sie könnten eine kleine Erweiterung von machen 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)
            ])
    }
}

Verwendung:

yourStackView.setBackgroundColor(.black)

1

Xamarin, C # -Version:

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

Unterklasse 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
}
}

Dann in deiner Klasse

yourStackView.backgroundColor = UIColor.lightGray

0

Sie können eine Unterschicht in StackView einfügen, es funktioniert für mich:

@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

Ich bin etwas skeptisch bei der Unterklassifizierung von UI-Komponenten. So benutze ich es,

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
        }
    }
}

Und das ist die Verwendung,

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

Hinweis: Wenn Sie bei diesem Ansatz einen Rahmen festlegen möchten, müssen Sie ihn in layoutMarginsder stackView festlegen, damit der Rahmen sichtbar ist.


0

Sie können der Stapelansicht keinen Hintergrund hinzufügen. Sie können jedoch eine Stapelansicht in eine Ansicht einfügen und dann den Hintergrund der Ansicht festlegen, um die Aufgabe zu erledigen. * Es wird den Fluss von Stackview nicht unterbrechen. Hoffe das wird helfen.


0

Wir können eine benutzerdefinierte Klasse StackViewwie diese haben:

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),
        ])
    }
}

Und es kann so verwendet werden:

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

Dies ist etwas besser als das Hinzufügen einer Erweiterungsfunktion, func addBackground(color: UIColor)wie von anderen vorgeschlagen. Die Hintergrundansicht ist faul, sodass sie erst erstellt wird, wenn Sie tatsächlich anrufen stackView.backgroundView.backgroundColor = .... Wenn Sie die Hintergrundfarbe mehrmals einstellen / ändern, werden nicht mehrere Unteransichten in die Stapelansicht eingefügt.


0

Wenn Sie vom Designer selbst aus steuern möchten, fügen Sie diese Erweiterung zur Stapelansicht hinzu

 @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

Sie könnten es so machen:

stackView.backgroundColor = UIColor.blue

Durch die Bereitstellung einer Erweiterung zum Überschreiben von 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
        }

    }

}

Sie müssen die richtige Position der Unteransicht einstellen, verwenden Sie Einfügen bei
Fattie

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.