I'm trying to add vertical lines, between labels inside a stack view all programatically.
The desired finish will be something like this image:
I can add the labels, all with the desired spacing; I can add horizontal lines but I can't figure out how to add those separator vertical lines in-between.
I'd like to do it something like this:
let stackView = UIStackView(arrangedSubviews: [label1, verticalLine, label2, verticalLine, label3])
Any hint?
Thanks
You can't use the same view in two places, so you'll need to create two separate vertical line views. You need to configure each vertical line view like this:
So add the labels one at a time to the stack view, and do something like this before adding each label to the stack view:
if stackView.arrangedSubviews.count > 0 {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = .black
stackView.addArrangedSubview(separator)
separator.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.6).isActive = true
}
Note that you do not want the vertical lines to be the same width as the labels, so you must not set the distribution
property of the stack view to fillEqually
. Instead, if you want all the labels to have equal width, you must create width constraints between the labels yourself. For example, after adding each new label, do this:
if let firstLabel = stackView.arrangedSubviews.first as? UILabel {
label.widthAnchor.constraint(equalTo: firstLabel.widthAnchor).isActive = true
}
Result:
Full playground code (updated to Swift 4.1 by Federico Zanetello):
import UIKit
import PlaygroundSupport
extension UIFont {
var withSmallCaps: UIFont {
let feature: [UIFontDescriptor.FeatureKey: Any] = [
UIFontDescriptor.FeatureKey.featureIdentifier: kLowerCaseType,
UIFontDescriptor.FeatureKey.typeIdentifier: kLowerCaseSmallCapsSelector]
let attributes: [UIFontDescriptor.AttributeName: Any] = [UIFontDescriptor.AttributeName.featureSettings: [feature]]
let descriptor = self.fontDescriptor.addingAttributes(attributes)
return UIFont(descriptor: descriptor, size: pointSize)
}
}
let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
rootView.backgroundColor = .white
PlaygroundPage.current.liveView = rootView
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .center
stackView.frame = rootView.bounds
rootView.addSubview(stackView)
typealias Item = (name: String, value: Int)
let items: [Item] = [
Item(name: "posts", value: 135),
Item(name: "followers", value: 6347),
Item(name: "following", value: 328),
]
let valueStyle: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 12).withSmallCaps]
let nameStyle: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12).withSmallCaps,
NSAttributedStringKey.foregroundColor: UIColor.darkGray]
let valueFormatter = NumberFormatter()
valueFormatter.numberStyle = .decimal
for item in items {
if stackView.arrangedSubviews.count > 0 {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = .black
stackView.addArrangedSubview(separator)
separator.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.4).isActive = true
}
let richText = NSMutableAttributedString()
let valueString = valueFormatter.string(for: item.value)!
richText.append(NSAttributedString(string: valueString, attributes: valueStyle))
richText.append(NSAttributedString(string: "\n" + item.name, attributes: nameStyle))
let label = UILabel()
label.attributedText = richText
label.textAlignment = .center
label.numberOfLines = 0
stackView.addArrangedSubview(label)
if let firstLabel = stackView.arrangedSubviews.first as? UILabel {
label.widthAnchor.constraint(equalTo: firstLabel.widthAnchor).isActive = true
}
}
UIGraphicsBeginImageContextWithOptions(rootView.bounds.size, true, 1)
rootView.drawHierarchy(in: rootView.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let png = UIImagePNGRepresentation(image)!
let path = NSTemporaryDirectory() + "/image.png"
Swift.print(path)
try! png.write(to: URL(fileURLWithPath: path))
You can try the following.
Thus you'll achieve the required scene. See these pictures for reference.
Here's a simple extension for adding separators between each row (NOTE! Rows, not columns as asked! Simple to modify for that case as well) . Basically same as accepted answer, but in a reusable format.
Use by calling e.g.
yourStackViewObjectInstance.addHorizontalSeparators(color : .black)
Extension:
extension UIStackView {
func addHorizontalSeparators(color : UIColor) {
var i = self.arrangedSubviews.count
while i >= 0 {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: i)
separator.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1).isActive = true
i -= 1
}
}
private func createSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With