Como alterar a cor de fundo do UIStackView?


159

Tentei alterar o UIStackViewplano de fundo de claro para branco no inspetor do Storyboard, mas ao simular, a cor do plano de fundo da exibição da pilha ainda tem uma cor clara.
Como posso alterar a cor de fundo de a UIStackView?


2
esse é um daqueles casos raros no SO, onde as respostas são simplesmente totalmente erradas . você acabou de adicionar uma ... visualização em segundo plano. é muito simples. artigo de exemplo que explica: useyourloaf.com/blog/stack-view-background-color
— Fattie

@Fattie Funcionou! Você pode adicioná-lo como resposta também?
— absina

oi @AbSin - as respostas de kurrodu e MariánČerný são perfeitas.
— Gordo

Haha, isso é ótimo elemento, não-prestação tem uma propriedade cor de fundo
— Brad Thomas

Respostas:


289

Você não pode fazer isso - UIStackViewé uma vista sem desenho, o que significa que drawRect()nunca é chamado e sua cor de fundo é ignorada. Se você deseja desesperadamente uma cor de fundo, considere colocar a vista da pilha dentro de outra UIViewe dar a ela uma cor de fundo.

Referência AQUI .

EDITAR:

Você pode adicionar um subView UIStackViewcomo mencionado AQUI ou nesta resposta (abaixo) e atribuir uma cor a ele. Confira abaixo extensionpara isso:

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

E você pode usá-lo como:

stackView.addBackground(color: .red)

12
isso está completamente errado . é um caso raro em que o artigo de Paul sobre HackingWithSwift está totalmente incorreto . basta adicionar outra visualização (que não é uma das visualizações organizadas ) e essa é a resposta.
— Fattie

11
aqui está um artigo que explica ele, é trivial: useyourloaf.com/blog/stack-view-background-color
— Fattie

1
Então, qual é o uso do stackview, não podemos simplesmente adicionar nossas visualizações dentro de outra visualização e fornecer restrições para alinhar horizontal ou verticalmente e, assim, evitar totalmente o stackview. Não é ridículo ter visualizações no stackview e no stackview dentro do UIView e depois adicionar esse UIView no nosso componente.
— Shivam Pokhriyal 12/09

Pelo menos, drawRect()é chamado. Você pode simplesmente testar por subclasseUIStackView
— khcpietro

Ótima resposta. Expandi um pouco, porque tenho vários temas no meu aplicativo para que a cor do plano de fundo possa mudar. Para impedir a adição de uma subvisão toda vez que a cor muda, altero a tag da subview para 123, e cada vez que a função é chamada, primeiro verifico se uma das subviews tem a tag 123 como esta if let backgroundView = self.subviews.first(where: {$0.tag == 123}) {. Nesse caso, altero a cor de fundo dessa exibição.
— 22619 guido

47

Eu faço assim:

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

Funciona como um encanto


Isso não funciona para mim quando usado em um storyboard, incluindo a resposta atualizada com os métodos get / set backgroundColor. : - / (iOS 10.2.1, Xcode 8.2.1, no iPad)
— webjprgm 15/17

Desculpas, eu descobri. O Interface Builder deixou de fora o campo "module" abaixo da classe, portanto não o carregou. Corrija isso e, em seguida, defina o plano de fundo em viewDidLoad do controlador de exibição.
— Webjprgm

1
Também faça sua classe IBDesignable -> @IBDesignable class StackViewO que permitirá que você crie no Storyboard . Não me importo muito em adicionar um plano de fundo em tempo de execução, mas é muito difícil projetar o stackview quando ele não possui cores no
— storyboard

@Attie Care para explicar por que é uma abordagem ruim?
— Arbitur

talvez Very Bad é um pouco @Arbitur dura :) :) mas não há necessidade de jogar com as camadas, uma vez que você acabou de adicionar outro ponto de vista (mas não um Arranged)
— Fattie

34

UIStackViewé um elemento que não é de renderização e, como tal, não é desenhado na tela. Isso significa que mudar backgroundColoressencialmente não faz nada. Se você quiser alterar a cor do plano de fundo, basta adicionar um UIViewa ela como uma subvisão (que não está organizada) como abaixo:

extension UIStackView {

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

}

1
esta resposta também é perfeita. pessoalmente, eu coloquei em todas as quatro restrições (como na resposta de kurrodu)
— Fattie

1
Supondo que você não altere a cor do plano de fundo, tudo bem. Mas se você chamar esse método várias vezes, as visualizações dos tempos anteriores ainda permanecerão. Também não há como remover facilmente a cor de fundo com esse método.
— keji

11

Talvez a maneira mais fácil, mais legível e menos invasiva seja incorporar o UIStackViewao UIViewe definir a cor do plano de fundo para a exibição.

E não se esqueça de configurar corretamente as restrições de Layout automático entre essas duas visualizações ... ;-)


2
Sim, mas onde está a diversão nisso? :-)
— webjprgm 15/02

1
A melhor solução, de longe - ele funciona no Interface Builder sozinho, sem uma linha de código
— Vladimir Grigorov

10

Pitiphong está correto, para obter um stackview com uma cor de fundo, faça algo como o seguinte ...

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

  stackView.insertSubview(bg, at: 0)

Isso fornecerá uma stackview cujo conteúdo será colocado em um fundo vermelho.

Para adicionar preenchimento ao stackview para que o conteúdo não fique alinhado com as bordas, adicione o seguinte no código ou no storyboard ...

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

2
Eu precisava usar stackView.insertSubview (bg, belowSubview: stackView.arrangedSubviews [0]) Caso contrário, a exibição colorida foi colocada no topo da pilha.
— ICyberPaul 29/03/19

esta resposta está quase correta, mas errada - você apenas usa a inserção em zero.
— Fattie

1
@iCyberPaul - você simplesmente usa a inserção em zero.
— Fattie

Observe o código de exemplo editado para inserir em 0, de modo que a visualização seja a última na hierarquia da visualização. (Eu disse que fazer algo como o seguinte ...)
— Michael Long

8

TL; DR: A maneira oficial de fazer isso é adicionando uma exibição vazia na exibição de pilha usando o addSubview:método e defina o fundo da exibição adicionada.

A explicação: UIStackView é uma subclasse UIView especial que apenas faz o layout não desenhar. Muitas de suas propriedades não funcionarão normalmente. E como o UIStackView fará o layout apenas de suas subvisões organizadas, isso significa que você pode simplesmente adicionar um UIView com o addSubview:método, definir suas restrições e cor de fundo. Esta é a maneira oficial de alcançar o que você deseja citar da sessão da WWDC


3

Isso funciona para mim no 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

No iOS10, a resposta do @ Arbitur precisa de um setNeedsLayout após a definição da cor. Esta é a mudança necessária:

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

Depende de como você configura as coisas, se você definir o backgroundColorantes de adicioná-lo a um superviewfunciona no ios10 e ios9, no entanto, se você atualizou o backgroundColorapós a layoutSubviews()função ter sido chamada, ele simplesmente não atualizaria backgroundColoro backgroundLayersignificado de nenhuma atualização visual. Corrigido agora, obrigado por apontar.
— Arbitur # 02/17

2

Aqui está uma breve visão geral para adicionar uma cor de fundo da exibição Pilha.

class RevealViewController: UIViewController {

    @IBOutlet private weak var rootStackView: UIStackView!

Criando vista de plano de fundo com cantos arredondados

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

Para fazer com que apareça como plano de fundo, nós o adicionamos à matriz de subvisões da visualização da pilha raiz no índice 0. Isso o coloca atrás das visualizações organizadas da visualização da pilha.

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

Adicione restrições para fixar o backgroundView nas bordas da exibição da pilha, usando uma pequena extensão no 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)
      ])
  }
}

chame o pinBackgrounddeviewDidLoad

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

Referência: AQUI


2

Você pode fazer uma pequena extensão de 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, versão 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

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

Então na sua aula

yourStackView.backgroundColor = UIColor.lightGray

0

Você pode inserir uma subcamada no StackView, funciona para mim:

@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

Sou um pouco cético em relação aos componentes de subclassificação da interface do usuário. É assim que eu estou 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 este é o 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: Com essa abordagem, se você deseja definir uma borda, é necessário definir layoutMarginsno stackView para que a borda fique visível.


0

Você não pode adicionar plano de fundo ao stackview. Mas o que você pode fazer é adicionar o stackview em uma exibição e, em seguida, definir o plano de exibição, isso fará o trabalho. * Não interromperá os fluxos do stackview. Espero que isso ajude.


0

Podemos ter uma classe personalizada StackViewcomo esta:

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 pode ser usado assim:

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

Isso é um pouco melhor do que adicionar uma função de extensão, func addBackground(color: UIColor)conforme sugerido por outras pessoas. A exibição em segundo plano é lenta, para que não seja criada até que você efetue a chamada stackView.backgroundView.backgroundColor = .... E definir / alterar a cor do plano de fundo várias vezes não resultará na inserção de várias subvisões na exibição da pilha.


0

Se você deseja controlar a partir do próprio designer, adicione esta extensão à exibição de pilha

 @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

Você poderia fazer assim:

stackView.backgroundColor = UIColor.blue

Fornecendo uma extensão para substituir 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
        }

    }

}

você deve definir a posição correta da subvisão, use insert at
— Fattie

-1

Experimente o BoxView . É semelhante ao UIStackView, mas é renderizado e suporta mais opções de layout.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.