Как изменить цвет фона UIStackView?

159

Я попытался изменить UIStackViewфон с прозрачного на белый в Инспекторе раскадровок, но при моделировании цвет фона стекового представления по-прежнему имеет чистый цвет.
Как я могу изменить цвет фона UIStackView?

LukasTong
источник
2
это один из тех редких случаев на SO, где ответы просто совершенно неверны . Вы просто добавляете ... вид фона. это очень просто пример статьи, объясняющей это: useyourloaf.com/blog/stack-view-background-color
Толстяк
@ Толстяк Это сработало! Можете ли вы добавить это как ответ?
Абсин
привет @AbSin - ответы kurrodu и MariánČerný идеальны.
Толстяк
Ха-ха, это отличный, не рендеринг элемент имеет свойство цвета фона
Брэд Томас,

Ответы:

289

Вы не можете сделать это - UIStackViewэто представление без рисунка, что означает, что drawRect()он никогда не вызывается, а его цвет фона игнорируется. Если вы отчаянно хотите получить цвет фона, подумайте о размещении стекового представления внутри другого UIViewи предоставлении этому виду фонового цвета.

Ссылка ЗДЕСЬ .

РЕДАКТИРОВАТЬ:

Вы можете добавить подпредставление, 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
это совершенно неправильно . это редкий случай, когда статья Пола о HackingWithSwift совершенно неверна . Вы просто добавляете другой вид (который не является одним из упорядоченных видов), и это ответ.
Толстяк
11
Вот статья, которая объясняет это, это тривиально: useyourloaf.com/blog/stack-view-background-color
Fattie
1
Тогда какая польза от стека, не можем ли мы просто добавить наши представления в другое представление и наложить на него ограничения, чтобы выровнять его по горизонтали или вертикали и таким образом полностью избежать стека. Разве не смешно иметь представления внутри стека и этот стека внутри UIView, а затем добавить этот UIView в наш компонент.
Шивам Похриял
По крайней мере, 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
    }
}

Работает как шарм

Arbitur
источник
Это не работает для меня, когда используется в раскадровке, включая обновленный ответ с помощью методов get / set backgroundColor. : - / (iOS 10.2.1, Xcode 8.2.1, на iPad)
webjprgm
Извиняюсь, я понял это. Интерфейсный Разработчик пропустил поле «module» ниже класса, поэтому не загружал его. Исправьте это, затем установите фон в view-контроллере viewDidLoad исправили его.
webjprgm
1
также сделайте свой класс IBDesignable -> @IBDesignable class StackViewкоторый позволит вам создавать на доске истории. Меня не волнует добавление фона во время выполнения, но очень сложно спроектировать вид стека, когда на раскадровке нет цвета
FlowUI. SimpleUITesting.com
@Fattie Care, чтобы объяснить, почему это плохой подход?
Арбитур
возможно, Very Bad немного грубоват @Arbitur :) :) но играть со слоями не нужно, так как вы просто добавляете другой вид (но не Arranged)
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)
Толстяк
1
Если вы не меняете цвет фона, это нормально. Но если вы вызываете этот метод несколько раз, все равно остаются прежние представления. Также нет способа легко удалить цвет фона с помощью этого метода.
Кеджи
11

Возможно, самый простой, более читаемый и менее хакерский способ - встроить UIStackViewобъект в UIViewи установить цвет фона для представления.

И не забудьте правильно настроить ограничения Auto Layout между этими двумя представлениями… ;-)

MonsieurDart
источник
2
Да, но где в этом веселье? :-)
webjprgm
1
Лучшее решение на сегодняшний день - оно работает только в Интерфейсном конструкторе, без какой-либо строки кода
Владимир Григоров
10

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)
Майкл Лонг
источник
2
Мне нужно было использовать stackView.insertSubview (bg, underSubview: stackView.arrangedSubviews [0]). В противном случае цветное представление было помещено поверх стека.
iCyberPaul
этот ответ почти правильный, но неправильный - вы просто используете вставку в ноль.
Толстяк
1
@iCyberPaul - вы просто используете вставку в ноль.
Толстяк
Обратите внимание на пример кода, отредактированного для вставки в 0, так что представление является последним в иерархии представлений. (Я сказал, что сделайте что-то вроде следующего ...)
Майкл Лонг
8

TL; DR: Официальный способ сделать это - добавить пустое представление в представление стека с помощью addSubview:метода и установить вместо него добавленный фон представления.

Объяснение: UIStackView - это специальный подкласс UIView, который только делает макет, а не рисует. Многие из его свойств не будут работать как обычно. А поскольку UIStackView будет размещать только упорядоченные подпредставления, это означает, что вы можете просто добавить к нему UIView с addSubview:методом, установить его ограничения и цвет фона. Это официальный способ достичь того, чего вы хотите, цитируемого на сессии 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()
    }
}
BruceLiu
источник
В зависимости от того, как вы установили вещи, если вы установите , backgroundColorпрежде чем добавить его к superviewон работает на ios10 и ios9, однако, если вы обновили backgroundColorпосле его layoutSubviews()функция была вызвана она просто Wouldnt обновляют backgroundColorиз backgroundLayerне означает никакого визуального обновления. Исправлено сейчас, спасибо за указание на это.
Арбитур
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)
}

Добавьте ограничения, чтобы прикрепить backgroundView к краям стекового представления, используя небольшое расширение в 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)
      ])
  }
}

позвонить pinBackgroundизviewDidLoad

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

Ссылка от: ЗДЕСЬ

kurrodu
источник
2

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);
Зуко
источник
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)
Нхон Нгуен
источник
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
Zeeshan
источник
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
wlgemini
источник
0

Я немного скептически отношусь к компонентам UC. Вот как я это использую,

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

Вы не можете добавить фон в стек просмотра. Но то, что вы можете сделать, это добавить представление стека в представление, а затем установить фон представления, и это сделает работу. * Это не будет прерывать потоки стека. Надеюсь, это поможет.

Номан Харун
источник
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
        }

    }

}
nmdias
источник
Вы должны установить правильную позицию подпредставления, используйте insert at
Fattie
-1

Попробуйте BoxView . Это похоже на UIStackView, но это рендеринг и поддерживает больше вариантов компоновки.

Владимир
источник