【问题标题】:How to change the background color of UIStackView?如何更改 UIStackView 的背景颜色?
【发布时间】:2016-04-24 10:16:02
【问题描述】:

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

【问题讨论】:

  • 这是 SO 上罕见的案例之一,其中的答案只是 完全错误。您只需添加一个 ... 背景视图。这很简单。解释它的示例文章:useyourloaf.com/blog/stack-view-background-color
  • @Fattie 成功了!您也可以将其添加为答案吗?
  • 嗨 @AbSin - kurrodu 和 MariánČerný 的答案是完美的。
  • 哈哈,太好了,非渲染元素有背景色属性

标签: ios swift uistackview


【解决方案1】:

你不能这样做——UIStackView 是一个非绘图视图,这意味着 drawRect() 永远不会被调用,它的背景颜色会被忽略。如果你 非常想要背景颜色,考虑放置堆栈视图 在另一个 UIView 中,并为该视图提供背景颜色。

来自HERE的参考。

编辑:

您可以将子视图添加到HEREHERE 中提到的this answer (below) 并为其分配颜色。看看下面的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)

【讨论】:

  • 这是大错特错。 Paul 关于 HackingWithSwift 的文章完全不正确的情况很少见。您只需添加另一个视图(它不是 排列的视图之一),这就是答案。
  • 这里有一篇文章解释,很琐碎:useyourloaf.com/blog/stack-view-background-color
  • 那么stackview有什么用,我们不能简单地将我们的视图添加到另一个视图中并给它约束以使其水平或垂直对齐,这样就完全避免了stackview。在stackview里面有view,在UIView里面有那个stackview,然后在我们的组件中添加这个UIView,这不是很荒谬吗?
  • 至少,drawRect() 被调用了。您可以通过子类化 UIStackView 来简单地测试
【解决方案2】:

我是这样做的:

@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 获取/设置方法的更新答案。 :-/ (iOS 10.2.1, Xcode 8.2.1, 在 iPad 上)
  • 抱歉,我想通了。 Interface Builder 省略了类下方的“模块”字段,因此没有加载它。修复它,然后在视图控制器的 viewDidLoad 中设置背景修复它。
  • 也让你的类 IBDesignable -> @IBDesignable class StackView 这将允许你在故事板上进行设计。我不太关心在运行时添加背景,但是当故事板上没有颜色时,很难设计堆栈视图
  • @Fattie Care 解释为什么这是一个不好的方法?
  • 也许非常糟糕有点苛刻@Arbitur :) :) 但没有必要玩图层,因为您只需添加另一个视图(但不是 Arranged 视图)
【解决方案3】:

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

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

【讨论】:

  • 取决于您的设置方式,如果您在将 backgroundColor 添加到 superview 之前设置它,则它适用于 ios10 和 ios9,但是,如果您在其后更新 backgroundColor layoutSubviews() 函数被调用它根本不会更新backgroundColorbackgroundLayer 意味着没有视觉更新。现已修复,谢谢指出。
【解决方案4】:

也许最简单、更易读且不那么花哨的方法是将UIStackView 嵌入UIView 并为视图设置背景颜色。

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

【讨论】:

  • 是的,但其中的乐趣在哪里? :-)
  • 迄今为止最好的解决方案——它只在 Interface builder 中工作,无需一行代码
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

    Pitiphong 是正确的,要获得具有背景颜色的堆栈视图,请执行以下操作...

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

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

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

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

    【讨论】:

    • 我需要使用 stackView.insertSubview(bg, belowSubview: stackView.arrangedSubviews[0]) 否则彩色视图被放置在堆栈的顶部。
    • 这个答案几乎是正确的,但是错误 - 你只是在零处使用插入。
    • @iCyber​​Paul - 您只需在零处使用插入。
    • 注意示例代码编辑为在 0 处插入,这样视图在视图层次结构中是最后一个。 (我确实说过做一些类似的事情......)
    【解决方案7】:
    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];
    

    【讨论】:

      【解决方案8】:

      这适用于 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)
      

      【讨论】:

        【解决方案9】:

        你可以这样做:

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

        【讨论】:

        • 你要设置正确的子视图位置,使用insert at
        【解决方案10】:

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

        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 处的根堆栈视图的 subviews 数组中。这将它放在堆栈视图的排列视图后面。

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

        viewDidLoad 调用pinBackground

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

        参考来自:HERE

        【讨论】:

          【解决方案11】:

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

          【讨论】:

          • 这个答案也很完美。我个人提出了所有四个约束(如 kurrodu 的回答)
          • 假设你不改变背景颜色这很好。但是,如果您多次调用此方法,则之前的视图仍然存在。也没有办法用这种方法轻松去除背景颜色。
          【解决方案12】:

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

          【讨论】:

            【解决方案13】:

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

            【讨论】:

              【解决方案14】:

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

              【讨论】:

                【解决方案15】:

                你可以做一个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)
                

                【讨论】:

                  【解决方案16】:

                  我对子类化 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,这样边框才可见。

                  【讨论】:

                    【解决方案17】:

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

                    【讨论】:

                      【解决方案18】:

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

                      【讨论】:

                        【解决方案19】:

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

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

                        【讨论】:

                          【解决方案20】:

                          值得指出的是,从 iOS 14 开始,UIStackViews 会渲染背景颜色。您可以使用 Background 属性从 Storyboard 中设置 UIStackView 的背景。

                          或在代码中:

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

                          【讨论】:

                            【解决方案21】:

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

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

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2011-11-11
                              • 1970-01-01
                              • 1970-01-01
                              • 2016-04-08
                              • 2018-02-13
                              • 2012-01-02
                              • 2022-01-21
                              • 2016-05-31
                              相关资源
                              最近更新 更多