Reputation: 1105
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
Reputation: 2288
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
.
Upvotes: 0
Reputation: 4239
use like this
loseWeight.layer.borderColor = UIColor.orange.cgColor
loseWeight.layer.borderWidth = 1
Upvotes: 1
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
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
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
Upvotes: 1
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
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
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:
Resulting in this result:
Here's a link to my github repo of this example so you can see the storyboard file.
Upvotes: 16
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
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