Adding stacks to a stack view programmatically

I have a stack view to which I add some buttons when the UI loads. I have made these work quite well as they are, but I want to simplify my interface. Instead of showing a button for each feed, I want to add a horizontal stack view showing a label and a stepper to alter the values for each feed type.

This is what I have now:
Code Block
struct FeedProduct {
var name : String
var code : String
}
var feedProducts = [
FeedProduct(name: "Oats", code: "oats"),
FeedProduct(name: "Straw", code: "straw"),
FeedProduct(name: "Salt", code: "salt")
]
var feedInventory: [ String : Int ] = [
    "straw" : 10,
    "oats" : 20,
"salt" : 0
]
func addButtons() {
    for (key,value) in feedInventory{
        let button = UIButton()
        button.addTarget(self, action: #selector(self.selectFeed(sender:)), for: .touchUpInside)
            let filteredProducts = feedProducts.filter { $0.code.contains(key)}
            for feedProduct in filteredProducts {
                if value > 0 {
                button.setTitle(" \(feedProduct.name) , amount: \(value)", for: UIControl.State.normal)
                        button.accessibilityIdentifier = feedProduct.code
                    feedStack.addArrangedSubview(button)
                }}}}
    @objc func selectFeed(sender: UIButton){
//this is where assign the selected feed type, and I had an external stepper button to alter the amount of feed
}
   


My plan is to change the addButtons function so that instead of adding buttons, it will add a horizontal stack view containing a label and a stepper for each feed above 0 in the inventory. This would make my interface much cleaner. Not sure if this is even possible, maybe there are better ways of doing it. Any tips would be really helpful!
Answered by OOPer in 660968022

it will add a horizontal stack view containing a label and a stepper

Why don't you do it as you describe?
Code Block
func addButtons() {
for (key,value) in feedInventory where value > 0 {
let filteredProducts = feedProducts.filter { $0.code.contains(key)}
for feedProduct in filteredProducts {
let horizontalStack = UIStackView()
horizontalStack.axis = .horizontal
horizontalStack.alignment = .firstBaseline
let label = UILabel(frame: .zero)
label.text = " \(feedProduct.name) , amount: \(value)"
//...
label.widthAnchor.constraint(equalToConstant: 200).isActive = true
horizontalStack.addArrangedSubview(label)
let stepper = UIStepper(frame: .zero)
//...
stepper.accessibilityLabel = "\(feedProduct.code) up/down"
horizontalStack.addArrangedSubview(stepper)
feedStack.addArrangedSubview(horizontalStack)
}
}
}


What is causing problem ?

You should
  • create a stackView

  • add the buttons inside

  • set constraints as needed.

This should help with a lot of sample code:
https://stackoverflow.com/questions/30728062/add-views-in-uistackview-programmatically
There aren't any problems per se, but I want to add a stepper to the right of each "button" and turn the button into a label instead. I haven't added two items per row in a stack view programmatically, so I'm not sure how to do that. I'll check out the link you sent
What I would do :
  • create UIViews (not stackView)

  • in each, add a label and a stepper

  • set the constraints as needed

  • add these UIViews in the stackView

Note: You could subclass UIView, (call it LabelStepperView for instance) to have a label and a stepper, and have code to update label when stepper changes. That would give a more structured code.
How do you add the items to the UIView?

Code Block  
    func addButtons() {
       let item = UIView()
        item.backgroundColor =  colorLiteral(red: 0.005273803137, green: 0.4785152674, blue: 0.3960535526, alpha: 1)
        feedStack.addArrangedSubview(item)
        let item2 = UILabel()
        item2.text = "hello"
        item.addArrangedSubview(item2) //this is the part I'm having trouble with.. how do I add item2 within item?
}


Here is a small example.

Created a LabelStepperView class. I add label and stepper here.

Code Block
class LabelStepperView: UIView {
var textForLabel : String?
var label: UILabel!
var stepper: UIStepper!
var viewFrame: CGRect?
var aleatColor : UIColor = .white
required init(coder aDecoder: NSCoder) {
let wholeFrame = viewFrame ?? .zero
super.init(coder: aDecoder)!
self.label = UILabel(frame: wholeFrame)
self.addSubview(label) // Add label
self.stepper = UIStepper(frame: wholeFrame) // Just to have something ; will be done in draw
stepper.maximumValue = 4.0
let action = UIAction(title: "Stepper") { (action) in
// Change color randomly
self.backgroundColor = self.aleatColor
}
self.stepper.addAction(action, for: .valueChanged)
self.addSubview(stepper) // add stepper
}
override func draw(_ rect: CGRect) {
// Drawing code
let redAleat = CGFloat.random(in: 0.5 ... 1.0)
let greenAleat = CGFloat.random(in: 0.5 ... 1.0)
let blueAleat = CGFloat.random(in: 0.5 ... 1.0)
let alphaAleat = CGFloat.random(in: 0.5 ... 1.0)
self.aleatColor = UIColor(red: redAleat, green: greenAleat, blue: blueAleat, alpha: alphaAleat)
let wholeFrame = self.bounds
var smallFrame = wholeFrame
var stepperFrame = wholeFrame
smallFrame.origin.y = wholeFrame.midY - 10
smallFrame.size.width = wholeFrame.width / 2
smallFrame.size.height = 20
label.frame = smallFrame
self.label.text = stepper.value > 0 ? "Now \(self.stepper.value)" : "Hello"
stepperFrame.origin.x = wholeFrame.width / 2 + 4
stepperFrame.origin.y = label.frame.origin.y
stepper.frame = stepperFrame
}
}

In the VC, I create a UIView of that class (I did it in IB for simplicity, but you can do it by code).

Code Block
class SomeViewController: UIViewController {
@IBOutlet weak var labelStepper: LabelStepperView!
override func viewDidLoad() {
super.viewDidLoad()
labelStepper.viewFrame = labelStepper.frame
labelStepper.textForLabel = "From Gear"
self.view.setNeedsDisplay()
}
}


In your case, you should add several LabelStepperView to your stack
feedStack.addArrangedSubview(labelStepperView)
Accepted Answer

it will add a horizontal stack view containing a label and a stepper

Why don't you do it as you describe?
Code Block
func addButtons() {
for (key,value) in feedInventory where value > 0 {
let filteredProducts = feedProducts.filter { $0.code.contains(key)}
for feedProduct in filteredProducts {
let horizontalStack = UIStackView()
horizontalStack.axis = .horizontal
horizontalStack.alignment = .firstBaseline
let label = UILabel(frame: .zero)
label.text = " \(feedProduct.name) , amount: \(value)"
//...
label.widthAnchor.constraint(equalToConstant: 200).isActive = true
horizontalStack.addArrangedSubview(label)
let stepper = UIStepper(frame: .zero)
//...
stepper.accessibilityLabel = "\(feedProduct.code) up/down"
horizontalStack.addArrangedSubview(stepper)
feedStack.addArrangedSubview(horizontalStack)
}
}
}


Thanks! That works nicely
Adding stacks to a stack view programmatically
 
 
Q