Can you give UIStackView borders?
Asked Answered
H

9

36

I'm trying to give 5 separate UIStackViews in my ViewController borders. I gave each of them an IBOutlet and then called them in viewDidLoad to use the layer property but to no avail. Is it not possible to give stack views borders, programatically?

Code:

@IBOutlet weak var stackView1: UIStackView!
@IBOutlet weak var stackView2: UIStackView!
@IBOutlet weak var stackView3: UIStackView!
@IBOutlet weak var stackView4: UIStackView!
@IBOutlet weak var stackView5: UIStackView!

override func viewDidLoad() {
    super.viewDidLoad()

    stackView1.layer.borderWidth = 5
    stackView2.layer.borderWidth = 5
    stackView3.layer.borderWidth = 5
    stackView4.layer.borderWidth = 5
    stackView5.layer.borderWidth = 5   
}
Habitual answered 10/12, 2015 at 21:7 Comment(0)
M
45

Unfortunately this can't be done. UIStackView is unusual in that it is a "non-rendering" view which performs layout (using Auto Layout constraints) but does not display itself. It has a layer like all UIViews, but it's ignored.

See the Apple doc under "Managing the Stack View's Appearance":

The UIStackView is a nonrendering subclass of UIView. It does not provide any user interface of its own. Instead, it just manages the position and size of its arranged views. As a result, some properties (like backgroundColor) have no affect on the stack view. Similarly, you cannot override layerClass, drawRect:, or drawLayer:inContext:

Marigolde answered 10/12, 2015 at 21:32 Comment(1)
As for how to add a border, it's trickier, can't just do it that way. You'd have to add another view and use layout contraints. Try and post a question with code if unsuccessful!Marigolde
O
16

Its possible to do this by having views inside the stack view be the borders. This can be a lot of work and there might be certain situations that either won't work or have to be worked around so it might not be worth the effort. You'll need to nest the stack views so you can provide borders in both the horizontal and vertical directions. In my Bordered Stack Views blog post I go into more detail about this. But basically I have regular views have a background set to the color of my choosing and I give height or width constraints of 1 depending on the direction of the stack view's axis. Here is the full hierarchy of a 2x2 grid built in interface builder:

view hierarchy

Resulting in this result:

enter image description here

Here's a link to my github repo of this example so you can see the storyboard file.

Oblique answered 17/3, 2016 at 23:23 Comment(1)
Oops I made a typo, fixedOblique
G
12

You can embed stackView inside a UIView, then set borders of that view (color, width, etc), and then constraint stackView to that UIView like top, left, right, height.

Gabfest answered 30/6, 2016 at 14:27 Comment(0)
R
6

Here's a handy chunk of code I found and use:

extension UIView {
    func addTopBorderWithColor(color: UIColor, width: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x:0,y: 0, width:self.frame.size.width, height:width)
        self.layer.addSublayer(border)
    }

    func addRightBorderWithColor(color: UIColor, width: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x: self.frame.size.width - width,y: 0, width:width, height:self.frame.size.height)
        self.layer.addSublayer(border)
    }

    func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x:0, y:self.frame.size.height - width, width:self.frame.size.width, height:width)
        self.layer.addSublayer(border)
    }

    func addLeftBorderWithColor(color: UIColor, width: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x:0, y:0, width:width, height:self.frame.size.height)
        self.layer.addSublayer(border)
    }

    func addMiddleBorderWithColor(color: UIColor, width: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color.cgColor
        border.frame = CGRect(x:self.frame.size.width/2, y:0, width:width, height:self.frame.size.height)
        self.layer.addSublayer(border)
    }
}

Simply use on any view like this:

bottomControls.addMiddleBorderWithColor(color: buttonBorderColor, width: 3.0)

Source: How to add only a TOP border on a UIButton?

Razo answered 9/3, 2018 at 17:25 Comment(2)
I tried this on a UIStackView and tried to give it top border but it's not workingIndifferent
using CALayer is only the way for sure.Abash
F
3

As indicated by others you cannot do this (for details see the answer by Clafou).

What you can do, however, is embed your stack view in another UIView; making modifications to the layer of the enclosing UIView.

Forjudge answered 14/8, 2018 at 19:32 Comment(0)
B
1

I think the easiest way to do it is by using no more labels or views with hight/width equals one to represent borders , I mean it is even easier than that via making use of SPACING attribute of stack views themselves . Just fill your stack and its substances , then make spacing one for outer vertical stack , also make spacing one for inner horizontal stacks , you get perfect result . Lastly for sake of giving a specific color to borders I maintained this using background view for the outer stckview , it just has same constraint like stack with background color as you wish to borders , idea is when you make spacing the spacing takes color of view behind the stack , that's it :D , kindly check results as in attached image and let me know if anything not clear enter image description here

Brainbrainard answered 31/10, 2018 at 12:33 Comment(0)
I
1

I have multiple UIStackViews inside a UIStackView.

I wanted a top and bottom border only for ONE of the UIStackViews in the stack so I added the UIStackView in question to a UIView with the background color set to the color of the top & bottom border color I wanted and replaced the bordered UIStackView in the arrangedSubviews with the UIView.

import UIKit
import Foundation

let goldBorderedUIView = UIView()

lazy var mainStackView: UIStackView =
    {
        let mainStack = UIStackView(arrangedSubviews: [goldBorderedUIView, stack2, stack3, stack 4])

        mainStack.translatesAutoresizingMaskIntoConstraints = false
        mainStack.axis = .vertical
        mainStack.spacing = 0.5
        mainStack.distribution = .fillEqually
        return mainStack
    }()


     func setupBorderdStack() { 

     NSLayoutConstraint.activate([
        borderedStackView.leadingAnchor.constraint(equalTo: goldBorderedUIView.leadingAnchor),
        borderedStackView.topAnchor.constraint(equalTo: goldBorderedUIView.topAnchor, constant: 5),
        borderedStackView.trailingAnchor.constraint(equalTo: goldBorderedUIView.trailingAnchor),
        borderedStackView.bottomAnchor.constraint(equalTo: goldBorderedUIView.bottomAnchor, constant: -5)
     ])
    }

     override func viewDidLoad() {
     super.viewDidLoad()

     setupBorderdStack()
    }
Iglesias answered 15/6, 2020 at 5:40 Comment(0)
D
1

use like this

loseWeight.layer.borderColor = UIColor.orange.cgColor
loseWeight.layer.borderWidth = 1

enter image description here

Divine answered 3/2, 2022 at 7:34 Comment(0)
C
0

The simplest way I've found to add a border to a UIStackView is to extend the stack view class and then add two layered views: the bottom one being the same size as the stack view, and the one on top that's used mask out the inside of the border, which is slightly smaller.

Here's the extension in Swift 5:

extension UIStackView {
    func addBorder(color: UIColor, backgroundColor: UIColor, thickness: CGFloat) {
        let insetView = UIView(frame: bounds)
        insetView.backgroundColor = backgroundColor
        insetView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        insertSubview(insetView, at: 0)

        let borderBounds = CGRect(
            x: thickness,
            y: thickness,
            width: frame.size.width - thickness * 2,
            height: frame.size.height - thickness * 2)

        let borderView = UIView(frame: borderBounds)
        borderView.backgroundColor = color
        borderView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        insertSubview(borderView, at: 0)
    }
}

Then you add the border with a call like this:

myStackView.addBorder(color: .lightGray, backgroundColor: .white, thickness: 2)
Cheeky answered 28/4, 2020 at 20:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.