pmoney13
pmoney13

Reputation: 1105

Can you give UIStackView borders?

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   
}

Upvotes: 36

Views: 35413

Answers (10)

ashokds
ashokds

Reputation: 2288

* Update 2024 *

With iOS 15, Swift 5: Usual Border properties work

Yes, it's simple to apply border to a UIStackView. In my case, I have a nested UIStackViews and I could set the borderColor, borderWidth and cornerRadius with following code:

   myStackView.borderColor = UIColor(red: 25.0/255.0, green: 24.0/255.0, blue: 35.0/255.0, alpha: 0.15)
   myStackView.borderWidth = 1.0
   myStackView.cornerRadius = 4.0

And this is how space around stack view content 24px is created.

   let myStackView = [oneStack, twoStack, threeStack].stack(axis: .vertical, distribution: .fill, alignment: .fill, spacing: 16)
   myStackView.layoutMargins = UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24)
   myStackView.isLayoutMarginsRelativeArrangement = true

If need to apply border to oneStack and others in above example, just need to extend the logic to these to set borderColor, borderWidth and cornerRadius.

Rendered StackView: enter image description here

Upvotes: 0

Rasel Khan
Rasel Khan

Reputation: 4239

use like this

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

enter image description here

Upvotes: 1

daj mi spokój
daj mi spokój

Reputation: 276

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

Upvotes: 1

MR_22
MR_22

Reputation: 39

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)

Upvotes: 0

IsPha
IsPha

Reputation: 419

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

Upvotes: 1

Spencer Hall
Spencer Hall

Reputation: 3739

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.

Upvotes: 3

devpreneur
devpreneur

Reputation: 139

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?

Upvotes: 6

Korey Hinton
Korey Hinton

Reputation: 2562

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.

Upvotes: 16

Filip Raj
Filip Raj

Reputation: 253

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.

Upvotes: 12

Clafou
Clafou

Reputation: 15400

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:

Upvotes: 45

Related Questions