วิธีการเปลี่ยนสีพื้นหลังของ UIStackView?


158

ฉันพยายามเปลี่ยนUIStackViewพื้นหลังจากสีใสเป็นสีขาวในตัวตรวจสอบสตอรีบอร์ด แต่เมื่อทำการจำลองสีพื้นหลังของมุมมองสแต็กยังคงมีสีที่ชัดเจน
ฉันจะเปลี่ยนสีพื้นหลังของ a ได้UIStackViewอย่างไร?


2
นี้เป็นหนึ่งในกรณีที่หายากในดังนั้นที่คำตอบจะมีเพียงแค่ผิดทั้งหมด คุณเพียงแค่เพิ่มมุมมองพื้นหลัง ... มันง่ายมาก ตัวอย่างบทความอธิบาย: useyourloaf.com/blog/stack-view-background-color
Fattie

@Fattie มันใช้งานได้! คุณสามารถเพิ่มเป็นคำตอบได้หรือไม่?
absin

สวัสดี @AbSin - คำตอบของคุโรรดูและมาเรียอันนีนั้นสมบูรณ์แบบ
Fattie

ฮ่าฮ่านี่เป็นองค์ประกอบที่ยอดเยี่ยมและไม่มีการเรนเดอร์มีคุณสมบัติสีพื้นหลัง
Brad Thomas

คำตอบ:


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
นี้คือการที่ไม่ถูกต้องค่อนข้าง มันเป็นกรณีที่หายากที่บทความของพอลใน HackingWithSwift เป็นที่ไม่ถูกต้องทั้งหมด คุณเพียงแค่เพิ่มอีกมุมมองหนึ่ง (ซึ่งไม่ได้เป็นหนึ่งในการจัดมุมมอง) และนั่นคือคำตอบ
Fattie

11
นี่คือบทความที่อธิบายว่ามันเป็นเรื่องเล็กน้อย: useyourloaf.com/blog/stack-view-background-color
Fattie

1
แล้วการใช้ stackview คืออะไรเราไม่สามารถเพิ่มมุมมองของเราในอีกมุมมองหนึ่งและให้ข้อ จำกัด ในการจัดแนวทั้งแนวนอนหรือแนวตั้ง ไม่ใช่เรื่องน่าขันที่จะมีมุมมองภายในสแต็กวิวและสแต็ควิวภายใน UIView แล้วเพิ่ม UIView นี้ในองค์ประกอบของเรา
Shivam Pokhriyal

อย่างน้อยdrawRect()ก็เรียกว่า คุณสามารถทดสอบโดย subclassingUIStackView
khcpietro

คำตอบที่ดี ฉันขยายออกไปเล็กน้อยเพราะฉันมีหลายธีมในแอพของฉันดังนั้นสีพื้นหลังอาจเปลี่ยนไป เพื่อป้องกันการเพิ่มมุมมองย่อยทุกครั้งที่มีการเปลี่ยนแปลงสีฉันเปลี่ยนแท็กของมุมมองย่อยเป็น 123 จากนั้นทุกครั้งที่เรียกใช้ฟังก์ชันฉันจะตรวจสอบก่อนว่าหนึ่งในการชมย่อยมีแท็ก 123 เช่นนี้if let backgroundView = self.subviews.first(where: {$0.tag == 123}) {หรือไม่ ถ้าเป็นเช่นนั้นฉันจะเปลี่ยนสีพื้นหลังของมุมมองนั้น
กุย

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

ทำงานเหมือนจับใจ


สิ่งนี้ไม่ได้ผลสำหรับฉันเมื่อใช้ในกระดานเรื่องราวรวมถึงคำตอบที่ปรับปรุงด้วยวิธีการ get / set backgroundColor : - / (iOS 10.2.1, Xcode 8.2.1, บน iPad)
webjprgm

ขอโทษฉันคิดออก เครื่องมือสร้างส่วนต่อประสานออกจากฟิลด์ "โมดูล" ด้านล่างคลาสดังนั้นจึงไม่โหลด แก้ไขที่จากนั้นตั้งค่าพื้นหลังใน viewDidLoad ของ view controller แก้ไข
webjprgm

1
ยังทำให้ชั้นเรียนของคุณ IBDesignable -> @IBDesignable class StackViewซึ่งจะช่วยให้คุณออกแบบบนกระดานเรื่องราว ฉันไม่สนใจมากสำหรับการเพิ่มพื้นหลังในเวลาทำงาน แต่มันยากมากที่จะออกแบบสแต็ควิวเมื่อไม่มีสีบนกระดานเรื่องราว
FlowUI SimpleUITesting.com

@Fattie Care อธิบายทำไมทำไมมันถึงเป็นวิธีการที่ไม่ดี?
Arbitur

อาจแย่มากเป็นบิตที่รุนแรง @Arbitur :) :) แต่ไม่จำเป็นต้องเล่นกับเลเยอร์เนื่องจากคุณเพียงแค่เพิ่มมุมมองอื่น (แต่ไม่ใช่การจัดเรียง)
Fattie

34

UIStackViewเป็นองค์ประกอบที่ไม่เรนเดอร์และดังนั้นจึงไม่ถูกวาดบนหน้าจอ ซึ่งหมายความว่าการเปลี่ยนแปลงbackgroundColorไม่ทำอะไรเลย หากคุณต้องการเปลี่ยนสีพื้นหลังเพียงเพิ่ม a UIViewเป็น subview (ที่ไม่ได้จัดเรียง) ดังด้านล่าง:

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

อาจเป็นวิธีที่ง่ายที่สุดอ่านได้ง่ายกว่าและแฮ็กน้อยกว่าก็คือการฝังUIStackViewลงในUIViewและตั้งค่าสีพื้นหลังเป็นมุมมอง

และอย่าลืมกำหนดค่าข้อ จำกัด เลย์เอาต์อัตโนมัติระหว่างสองมุมมองเหล่านั้น ... ;-)


2
ใช่ แต่ความสนุกในนั้นอยู่ที่ไหน :-)
webjprgm

1
ทางออกที่ดีที่สุดโดยไกล - ใช้งานได้ในตัวสร้างส่วนต่อประสานเพียงอย่างเดียวโดยไม่ต้องใช้โค้ด
Vladimir Grigorov

10

ปิติพงษ์ถูกต้องเพื่อให้ได้สแต็ควิวที่มีสีพื้นหลังทำสิ่งต่อไปนี้ ...

  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)

2
ฉันจำเป็นต้องใช้ stackView.insertSubview (bg ด้านล่างSubview: stackView.arrangedSubviews [0]) ไม่เช่นนั้นมุมมองสีจะถูกวางไว้ด้านบนสุดของสแต็ก
iCyberPaul

คำตอบนี้เกือบจะถูกต้อง แต่ผิด - คุณใช้คำสั่ง insert ที่ศูนย์
Fattie

1
@iCyberPaul - คุณเพียงแค่ใช้การแทรกที่ศูนย์
Fattie

หมายเหตุตัวอย่างโค้ดที่แก้ไขเพื่อแทรกที่ 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()
    }
}

ขึ้นอยู่กับว่าคุณตั้งค่าสิ่งขึ้นถ้าคุณตั้งค่าbackgroundColorก่อนที่คุณจะเพิ่มลงในsuperviewการทำงานบน ios10 และ ios9 แต่ถ้าคุณปรับปรุงbackgroundColorหลังของlayoutSubviews()ฟังก์ชั่นได้รับการเรียกมันว่าการปรับปรุง wouldnt เพียงbackgroundColorของbackgroundLayerความหมายการปรับปรุงภาพไม่มี แก้ไขแล้วขอบคุณที่ชี้ให้เห็น
Arbitur

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

อ้างอิงจาก: ที่นี่


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

คุณสามารถแทรก sublayer ไปยัง 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.