Làm cách nào để thay đổi màu nền của UIStackView?


157

Tôi đã cố gắng thay đổi UIStackViewnền từ rõ ràng sang trắng trong trình kiểm tra Storyboard, nhưng khi mô phỏng, màu nền của chế độ xem ngăn xếp vẫn có màu rõ ràng.
Làm thế nào tôi có thể thay đổi màu nền của a UIStackView?


2
đây là một trong những trường hợp hiếm hoi trên SO, trong đó câu trả lời đơn giản là hoàn toàn sai . bạn chỉ cần thêm một ... chế độ xem nền. nó rất đơn giản. bài viết ví dụ giải thích về nó: useyourloaf.com/blog/stack-view-background-color
Fattie

@Fattie Nó hoạt động! Bạn có thể thêm nó như một câu trả lời không?
absin

hi @AbSin - câu trả lời của Kurrodu và MariánČerný là hoàn hảo.
Fattie

Haha, đây là yếu tố tuyệt vời, không kết xuất có thuộc tính màu nền
Brad Thomas

Câu trả lời:


289

Bạn không thể làm điều này - UIStackViewlà chế độ xem không vẽ, nghĩa drawRect()là không bao giờ được gọi và màu nền của nó bị bỏ qua. Nếu bạn rất muốn có một màu nền, hãy xem xét việc đặt chế độ xem ngăn xếp bên trong một khung nhìn khác UIViewvà cung cấp cho chế độ xem đó một màu nền.

Tham khảo từ ĐÂY .

BIÊN TẬP:

Bạn có thể thêm một Chế độ xem phụ UIStackViewnhư được đề cập TẠI ĐÂY hoặc trong câu trả lời này (bên dưới) và gán màu cho nó. Kiểm tra dưới đây extensioncho rằng:

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

Và bạn có thể sử dụng nó như:

stackView.addBackground(color: .red)

12
Điều này khá sai . đó là một trường hợp hiếm hoi khi bài viết của Paul về HackingWithSwift hoàn toàn không chính xác . bạn chỉ cần thêm một chế độ xem khác ( không phải là một trong các chế độ xem được sắp xếp ) và đó là câu trả lời.
Fattie

11
Đây là một bài viết giải thích nó, nó là tầm thường: useyourloaf.com/blog/stack-view-background-color
Fattie

1
Vậy thì việc sử dụng stackview là gì, chúng ta không thể đơn giản thêm các khung nhìn của mình vào một khung nhìn khác và đưa ra các ràng buộc để căn chỉnh nó theo chiều ngang hoặc chiều dọc và theo cách đó hoàn toàn tránh stackview. Không có gì vô lý khi có các khung nhìn bên trong stackview và stackview bên trong UIView và sau đó thêm UIView này trong thành phần của chúng tôi.
Shivam Pokhriyal

Ít nhất, drawRect()được gọi là. Bạn chỉ có thể kiểm tra bằng cách phân lớpUIStackView
khcpietro

Câu trả lời chính xác. Tôi đã mở rộng nó một chút, vì tôi có một số chủ đề trong ứng dụng của mình để màu nền có thể thay đổi. Để ngăn việc thêm một khung nhìn phụ mỗi khi màu thay đổi, tôi thay đổi thẻ của khung nhìn phụ thành 123, sau đó mỗi lần hàm được gọi, trước tiên tôi kiểm tra xem một trong các khung nhìn có thẻ 123 như thế này không if let backgroundView = self.subviews.first(where: {$0.tag == 123}) {. Nếu vậy, tôi thay đổi màu nền của khung nhìn đó.
guido

47

Tôi làm như thế này:

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

Hoạt động như một lá bùa


Điều này không hoạt động đối với tôi khi được sử dụng trong bảng phân cảnh, bao gồm câu trả lời được cập nhật với các phương thức get / set backgroundColor. : - / (iOS 10.2.1, Xcode 8.2.1, trên iPad)
webjprgm

Xin lỗi, tôi đã tìm ra nó. Trình tạo giao diện bỏ qua trường "mô-đun" bên dưới lớp nên không tải nó. Khắc phục sự cố đó, sau đó đặt nền trong viewDidLoad của trình điều khiển xem đã sửa nó.
webjprgm

1
cũng làm cho lớp IBDesignable của bạn -> @IBDesignable class StackViewĐiều này sẽ cho phép bạn thiết kế trên bảng Story. Tôi không quan tâm lắm đến việc thêm một nền trong thời gian chạy, nhưng rất khó để thiết kế stackview khi nó không có màu trên bảng câu chuyện
FlowUI. SimpleUITesting.com

@Fattie Chăm sóc để giải thích tại sao đó là một cách tiếp cận tồi?
Arbitur

có lẽ Very Bad là một chút khắc nghiệt @Arbitur :) :) nhưng không cần phải chơi với các lớp, vì bạn chỉ cần thêm một chế độ xem khác (nhưng không phải là một sắp xếp)
Fattie

34

UIStackViewlà một yếu tố không hiển thị và như vậy, nó không được vẽ trên màn hình. Điều này có nghĩa là thay đổi backgroundColorvề cơ bản không làm gì cả. Nếu bạn muốn thay đổi màu nền, chỉ cần thêm một màu UIViewdưới dạng một khung nhìn phụ (không được sắp xếp) như dưới đây:

extension UIStackView {

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

}

1
Câu trả lời này cũng hoàn hảo. cá nhân tôi đặt trong tất cả bốn ràng buộc (như trong câu trả lời của Kurrodu)
Fattie 19/12/17

1
Giả sử bạn không thay đổi màu nền thì điều này là tốt. Nhưng nếu bạn gọi phương thức này nhiều lần thì các lượt xem từ các lần trước vẫn còn. Ngoài ra, không có cách nào để dễ dàng loại bỏ màu nền bằng phương pháp này.
keji

11

Có lẽ cách dễ nhất, dễ đọc hơn và ít hack hơn sẽ được nhúng UIStackViewvàoUIView và đặt màu nền cho chế độ xem.

Và đừng quên cấu hình đúng các ràng buộc Bố cục tự động giữa hai chế độ xem này ;-)


2
Vâng, nhưng niềm vui trong đó là ở đâu? :-)
webjprgm

1
Giải pháp tốt nhất cho đến nay - nó chỉ hoạt động trong Trình tạo giao diện, không có dòng mã
Vladimir Grigorov

10

Pitiphong là chính xác, để có được một stackview với màu nền hãy làm một cái gì đó như sau ...

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

  stackView.insertSubview(bg, at: 0)

Điều này sẽ cung cấp cho bạn một stackview có nội dung sẽ được đặt trên nền đỏ.

Để thêm phần đệm vào stackview để nội dung không bị lộn với các cạnh, hãy thêm đoạn mã sau hoặc trên bảng phân cảnh ...

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

2
Tôi cần sử dụng stackView.insertSubview (bg, bên dướiSubview: stackView.arTHERSubview [0]) Nếu không, chế độ xem màu được đặt trên đầu ngăn xếp.
iCyberPaul

Câu trả lời này gần như đúng, nhưng sai - bạn chỉ cần sử dụng insert ở mức 0.
Fattie

1
@iCyberPaul - bạn chỉ cần sử dụng insert ở mức 0.
Fattie

Lưu ý mã mẫu được chỉnh sửa để chèn tại 0 sao cho chế độ xem cuối cùng trong phân cấp chế độ xem. (Tôi đã nói làm một cái gì đó như sau ...)
Michael Long

8

TL; DR: Cách chính thức để thực hiện việc này là bằng cách thêm chế độ xem trống vào chế độ xem ngăn xếp bằng cách sử dụng addSubview: phương thức và đặt nền chế độ xem được thêm vào thay thế.

Giải thích: UIStackView là một lớp con UIView đặc biệt chỉ làm bố cục không vẽ. Vì vậy, nhiều tài sản của nó sẽ không hoạt động như bình thường. Và vì UIStackView sẽ chỉ bố trí các bản xem trước được sắp xếp của nó, điều này có nghĩa là bạn chỉ cần thêm nó vào UIView bằng addSubview:phương thức, đặt các ràng buộc và màu nền của nó. Đây là cách chính thức để đạt được những gì bạn muốn trích dẫn từ phiên WWDC


3

Điều này hoạt động với tôi trong Swift 3 và 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

Trong iOS10, câu trả lời của @ Arbitur cần một setNeedLayout sau khi màu được đặt. Đây là thay đổi cần thiết:

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

Tuy nhiên, phụ thuộc vào cách bạn thiết lập mọi thứ, nếu bạn đặt backgroundColortrước khi bạn thêm nó vào superviewnó hoạt động trên ios10 và ios9, tuy nhiên, nếu bạn cập nhật backgroundColorsau khi layoutSubviews()chức năng của nó được gọi thì đơn giản là nó sẽ không cập nhật nghĩa backgroundColorcủa việc backgroundLayerkhông cập nhật trực quan. Đã sửa bây giờ, cảm ơn vì đã chỉ ra.
Arbitur

2

Dưới đây là một tổng quan ngắn gọn để thêm Màu nền cho chế độ xem Stack.

class RevealViewController: UIViewController {

    @IBOutlet private weak var rootStackView: UIStackView!

Tạo chế độ xem nền với các góc tròn

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

Để làm cho nó xuất hiện dưới dạng nền, chúng ta thêm nó vào mảng xem trước của khung nhìn ngăn xếp gốc ở chỉ mục 0. Điều đó đặt nó phía sau các khung nhìn được sắp xếp của khung nhìn ngăn xếp.

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

Thêm các ràng buộc để ghim nềnView vào các cạnh của chế độ xem ngăn xếp, bằng cách sử dụng một phần mở rộng nhỏ trên 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)
      ])
  }
}

gọi pinBackgroundtừviewDidLoad

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

Tham khảo từ: TẠI ĐÂY


2

Bạn có thể thực hiện một phần mở rộng nhỏ của 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)
            ])
    }
}

Sử dụng:

yourStackView.setBackgroundColor(.black)

1

Phiên bản 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

Phân lớp 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
}
}

Sau đó vào lớp của bạn

yourStackView.backgroundColor = UIColor.lightGray

0

Bạn có thể chèn một lớp con vào StackView, nó hoạt động với tôi:

@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

Tôi có một chút hoài nghi trong các thành phần UI phân lớp. Đây là cách tôi đang sử dụng nó,

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

Và đây là cách sử dụng,

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

Lưu ý: Với phương pháp này, nếu bạn muốn đặt đường viền, bạn phải đặt layoutMarginstrên stackView để đường viền hiển thị.


0

Bạn không thể thêm nền vào stackview. Nhưng những gì bạn có thể làm là thêm stackview trong một khung nhìn và sau đó đặt nền của khung nhìn này sẽ hoàn thành công việc. * Nó sẽ không làm gián đoạn dòng stackview. Hy vọng điều này sẽ giúp.


0

Chúng ta có thể có một lớp tùy chỉnh StackViewnhư thế này:

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

Và nó có thể được sử dụng như thế này:

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

Điều này tốt hơn một chút so với việc thêm chức năng mở rộng func addBackground(color: UIColor)theo đề xuất của người khác. Chế độ xem nền là lười biếng để nó sẽ không được tạo cho đến khi bạn thực sự gọi stackView.backgroundView.backgroundColor = .... Và cài đặt / thay đổi màu nền nhiều lần sẽ không dẫn đến nhiều lượt xem được chèn trong chế độ xem ngăn xếp.


0

Nếu bạn muốn kiểm soát từ chính nhà thiết kế, hãy thêm tiện ích mở rộng này vào chế độ xem ngăn xếp

 @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

Bạn có thể làm như thế này:

stackView.backgroundColor = UIColor.blue

Bằng cách cung cấp một phần mở rộng để ghi đè lên 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
        }

    }

}

bạn phải đặt đúng vị trí của khung nhìn phụ, sử dụng chèn tại
Fattie

-1

Hãy thử BoxView . Nó tương tự như UIStackView, nhưng nó được kết xuất và hỗ trợ nhiều tùy chọn bố cục hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.