如何更改 UIStackView 的背景颜色?

我尝试在故事板检查器中将 UIStackView背景从清晰变为白色,但是当模拟时,堆栈视图的背景颜色仍然是清晰的。
如何更改 UIStackView的背景颜色?

78126 次浏览

你不能这样做-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)

我是这样做的:

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

非常有效

在 iOS10中,@Arbitur 的答案在设置颜色后需要一个 setNeedsLayout:

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

也许最简单、可读性更好、不那么拙劣的方法是将 UIStackView嵌入到 UIView中,然后将背景颜色设置为视图。

不要忘记正确配置这两个视图之间的 Auto Layout 约束... ; -)

DR: 正式的方法是使用 addSubview:方法将一个空视图添加到堆栈视图中,然后设置添加的视图背景。

解释: UIStackView 是一个特殊的 UIView 子类,它只做布局而不绘图。它的许多属性不会像往常一样工作。而且由于 UIStackView 将布局它的排列子视图只,这意味着您可以简单地添加它一个 UIView 与 addSubview:方法,设置其约束和背景颜色。这是官方的方式来实现你想要的 引自 WWDC 会议

Pitiphong 是正确的,得到一个背景颜色的堆栈视图做一些像下面这样的事情..。

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


stackView.insertSubview(bg, at: 0)

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

要向堆栈视图添加填充,以使内容不与边缘齐平,请在代码或情节串连板中添加以下内容..。

  stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
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];

这对我在 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)

你可以这样做:

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
}


}


}

下面是添加 Stack 视图背景颜色的简要概述。

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

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

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

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

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

viewDidLoad呼叫 pinBackground

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

参考文献: 给你

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


}

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);

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

你可以在 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

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

我有点怀疑子类化 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

注意: 使用这种方法,如果您想设置边界,您必须在 stackView 上设置 layoutMargins,以便边界可见。

不能向 stackview 添加背景。 但是你可以在视图中添加 stackview,然后设置视图的背景,这样就可以完成工作了。 不会打断 Stackview 的流动 * 。 希望这个能帮上忙。

我们可以有这样一个自定义类 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 = ...之前不会创建它。多次设置/更改背景颜色不会导致在堆栈视图中插入多个子视图。

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

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

值得指出的是,从 IOS14开始,UIStackView 确实会渲染背景颜色。您可以设置背景属性从故事板 UIStackView 的背景。

enter image description here

或者用以下代码:

if #available(iOS 14.0, *) {
stackView.backgroundColor = .green
} else {
// Fallback for older versions of iOS
}

有很多好的答案,但是我发现它们并不完整,所以我的版本是基于其中最好的一个:

    /// This extension addes missing background color to stack views on iOS 13 and earlier
extension UIStackView {
    

private struct CustomAttributeNames {
static var _backgroundView = "_backgroundView"
}
    

@IBInspectable var customBackgroundColor: UIColor? {
get { backgroundColor }
set { setBackgroundColor(newValue) }
}
    

var backgroundView: UIView {
if let view = objc_getAssociatedObject(self, &CustomAttributeNames._backgroundView) as? UIView {
return view
}
        

let view = UIView(frame: bounds)
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        

insertSubview(view, at: 0)
        

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

return view
}
    

func setBackgroundColor(_ color: UIColor?) {
backgroundColor = color
backgroundView.backgroundColor = color
}
}

苹果文档中的解释是,栈视图本身在 iOS13中从未呈现过——它的目的是管理其已安排好的子视图:

UIStackView 是 UIView 的非呈现子类; 也就是说,它不提供自己的任何用户界面。相反,它只管理其排列好的视图的位置和大小。因此,某些属性(如 backoundColor)对堆栈视图没有影响。

你可以通过创建一个扩展来修复这个问题,只是为了修复 iOS13或以下版本的背景颜色:

import UIKit


extension UIStackView {
// MARK: Stored properties


private enum Keys {
static var backgroundColorView: UInt8 = 0
}


private var backgroundColorView: UIView? {
get {
objc_getAssociatedObject(self, &Keys.backgroundColorView) as? UIView
}
set {
objc_setAssociatedObject(self, &Keys.backgroundColorView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}


override open var backgroundColor: UIColor? {
didSet {
// UIKit already support setting background color in iOS 14 or above
guard #available(iOS 14.0, *) else {
// fix setting background color directly to stackview by add a background view
if backgroundColorView == nil {
let backgroundColorView = UIView(frame: bounds)
backgroundColorView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
insertSubview(backgroundColorView, at: 0)
self.backgroundColorView = backgroundColorView
}
backgroundColorView?.backgroundColor = backgroundColor
return
}
}
}
}