如何更改UIStackView的背景颜色?


158

我试图UIStackView在Storyboard检查器中将背景从透明更改为白色,但是在模拟时,堆栈视图的背景颜色仍然具有清晰的颜色。
如何更改的背景颜色UIStackView


2
这是SO上罕见的情况之一,答案完全是错误的。您只需添加一个...背景视图。这很简单。例如文章,解释它:useyourloaf.com/blog/stack-view-background-color
Fattie

@Fattie奏效了!您也可以将其添加为答案吗?
苦艾酒

嗨@AbSin-kurrodu和MariánČerný的答案非常完美。
Fattie

哈哈,这是很棒的非渲染元素,具有背景颜色属性
Brad Thomas

Answers:


289

您不能执行此操作– UIStackView是非绘图视图,这意味着drawRect()从不调用该视图, 并且其背景颜色将被忽略。如果您非常需要背景颜色,请考虑将堆栈视图放置在另一个视图中UIView,并为该视图提供背景颜色。

参考从这里

编辑:

您可以在此处下面的答案中(如下)将subView添加到其中UIStackView,并为其分配颜色。请查看以下内容:extension

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

您可以像这样使用它:

stackView.addBackground(color: .red)

12
这是完全错误的。在Paul关于HackingWithSwift的文章完全不正确的情况下,这种情况很少见。只需添加另一种观点(这是没有的一个安排意见),这就是答案。
Fattie

11
这里,解释它的文章,实在是小巫见大巫: useyourloaf.com/blog/stack-view-background-color
Fattie

1
那么stackview的用途是什么,我们不能简单地将视图添加到另一个视图中并赋予其约束以使其水平或垂直对齐,这样就完全避免了stackview。在stackview内和UIView内都具有该视图,然后在我们的组件中添加该UIView是不是很荒谬的。
Shivam Pokhriyal,

至少drawRect()被称为。您可以简单地通过子类化进行测试UIStackView
khcpietro,

好答案。我对其进行了扩展,因为我的应用程序中有多个主题,因此背景颜色可能会发生变化。为了防止每次更改颜色时添加子视图,我将子视图的标签更改为123,然后每次调用该函数时,我首先检查一个子视图是否具有这样的标签123 if let backgroundView = self.subviews.first(where: {$0.tag == 123}) {。如果是这样,我将更改该视图的背景色。
guido

47

我这样做是这样的:

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

奇迹般有效


在情节提要中使用时,这对我不起作用,包括使用backgroundColor get / set方法更新的答案。:-/(iOS 10.2.1,Xcode 8.2.1,在iPad上)
webjprgm

抱歉,我知道了。Interface Builder忽略了类下方的“模块”字段,因此未加载它。修复该问题,然后在视图控制器的viewDidLoad中设置背景,将其修复。
webjprgm

1
还使您的课程IBDesignable-> @IBDesignable class StackView允许您在Story Board上进行设计。我不太关心在运行时添加背景,但是当故事板上没有颜色时,很难设计Stackview
FlowUI。SimpleUITesting.com 17-4-22

@Fattie Care解释为什么这是一种不好的方法?
Arbitur

也许Very Bad是有点苛刻的@Arbitur :) :) :)但是没有必要去玩这些图层,因为您只需添加另一个视图(而不是一个已排列的视图)
Fattie

34

UIStackView是一个非渲染元素,因此不会在屏幕上绘制。这意味着更改backgroundColor本质上没有任何作用。如果要更改背景颜色,只需将其添加UIView为子视图(未排列),如下所示:

extension UIStackView {

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

}

1
这个答案也很完美。我个人把全部四个约束(如在kurrodu答案)
Fattie

1
假设您不更改背景颜色,这很好。但是,如果多次调用此方法,则以前的视图仍然保留。同样,也没有办法用这种方法轻松去除背景色。
keji

11

也许最简单,更易读,更不容易破解的方法是将嵌入UIStackViewUIView并将背景颜色设置为视图。

并且不要忘记在这两个视图之间正确配置自动布局约束…;-)


2
是的,但是那有什么乐趣呢?:-)
webjprgm

1
迄今为止最好的解决方案-它仅在Interface builder中运行,而无需一行代码
Vladimir Grigorov

10

Pitiphong是正确的,要获取具有背景色的stackview,请执行以下操作...

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

  stackView.insertSubview(bg, at: 0)

这将为您提供一个stackview,其内容将放置在红色背景上。

要将填充添加到stackview中,以使内容不与边缘齐平,请在代码中或故事板上添加以下内容...

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

2
我需要使用stackView.insertSubview(bg,belowSubview:stackView.arrangedSubviews [0])否则将彩色视图放置在堆栈顶部。
iCyber​​Paul

这个答案几乎是正确的,但却是错误的 -您只需在零处使用insert即可。
Fattie

1
@iCyber​​Paul-您只需在零处使用插入。
Fattie

请注意,示例代码已编辑为确实要插入0,以使视图位于视图层次结构的最后。(我没有说做一些下面...)
迈克尔龙

8

TL; DR:执行此操作的官方方法是使用addSubview:方法将一个空视图添加到堆栈视图中,并设置添加的视图背景。

说明:UIStackView是一个特殊的UIView子类,仅执行布局而不绘制。因此,它的许多属性将无法正常工作。而且由于UIStackView将仅布局其排列的子视图,所以这意味着您可以简单地使用addSubview:方法添加UIView ,设置其约束和背景色。这是实现WWDC会话中引用的官方方法


3

这适用于Swift 3和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

在iOS10中,@ Arbitur的答案在设置颜色后需要setNeedsLayout。这是需要进行的更改:

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

取决于如何设置,如果backgroundColor在将其添加到a之前进行设置,则superview它可以在ios10和ios9上运行,但是,如果在调用backgroundColorlayoutSubviews()功能后更新了它,则只会更新没有视觉更新backgroundColorbackgroundLayer含义。已修复,感谢您指出。
Arbitur '17

2

这是添加堆栈视图背景颜色的简要概述。

class RevealViewController: UIViewController {

    @IBOutlet private weak var rootStackView: UIStackView!

创建带有圆角的背景视图

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

为了使其显示为背景,我们将其添加到根堆栈视图的subviews数组中的索引0。这将其置于堆栈视图的排列视图之后。

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

通过在UIView上使用一个小的扩展,添加约束以将backgroundView固定到堆栈视图的边缘。

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

调用pinBackgroundviewDidLoad

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

参考来自:这里


2

您可以对 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)
            ])
    }
}

用法:

yourStackView.setBackgroundColor(.black)

1

Xamarin,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

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

那你上课

yourStackView.backgroundColor = UIColor.lightGray

0

您可以在StackView中插入一个子层,它对我有用:

@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

我对子类化UI组件有些怀疑。这就是我的使用方式

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

这就是用法

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

注意:使用这种方法时,如果要设置边框,则必须layoutMargins在stackView上进行设置,以使边框可见。


0

您不能将背景添加到stackview。但是您可以做的是在视图中添加stackview,然后设置视图背景,这将完成工作。*它不会中断stackview的流程。希望这会有所帮助。


0

我们可以有一个这样的自定义类StackView

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

可以这样使用:

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

这比func addBackground(color: UIColor)其他人建议的扩展功能要好。背景视图是惰性的,因此在实际调用之前不会创建它stackView.backgroundView.backgroundColor = ...。并且多次设置/更改背景颜色不会导致在堆栈视图中插入多个子视图。


0

如果要从设计器本身进行控制,请将此扩展添加到堆栈视图

 @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

您可以这样做:

stackView.backgroundColor = UIColor.blue

通过提供扩展以覆盖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
        }

    }

}

您必须设置子视图的正确位置,在
Fattie 17'Aug

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.