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?

LukasTong
fonte
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)
Dharmesh Kheni
fonte
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

Arbitur
fonte
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)
    }

}
Marián Černý
fonte
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 ... ;-)

MonsieurDart
fonte
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)
Michael Long
fonte
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

Pitiphong Phongpattranont
fonte
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)
Murray Sagal
fonte
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()
    }
}
BruceLiu
fonte
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

kurrodu
fonte
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)
Nhon Nguyen
fonte
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);
zuko
fonte
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];
Roman Solodyashkin
fonte
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
Zeeshan
fonte
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
wlgemini
fonte
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.

Loop infinito
fonte
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.

Noman Haroon
fonte
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.

Yuchen Zhong
fonte
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)
     }
 }
Milind Chaudhary
fonte
-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
        }

    }

}
nmdias
fonte
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.

Vladimir
fonte