0

My goal is to create buttons the green box is shown. This green box is

@IBOutlet weak var categoryView: UIView!

a referenced UIView. I am trying to add buttons that can go to different Categories shown below. Ideally, I would like the buttons to be centered in the green space with 10 constraints to the sides. The current "name as shown on card" was a test run added just to make sure that my stack-view is properly applied. Therefore, something must be wrong with creating the buttons. However, I am not sure how it is wrong. Potentially I would like to make this view freeform and that if there was a lot of categories, it can go off the current view. Any advise is appreciated

Lastly, I have a few concerns on when creating the button, there is only one onclick listener. However, This onclick listener has different pointers, such as the first button created will go change the view to row 0 of the table (Accessories), but the second one should go to Appetizers which is row 1. and the third button should go to Deserts at row 3. Etc.

data Refference

The data im working is looks like this. However, this is depends on the backend retrieval and can be different everytime

> [Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Accessories"), menu_food_id: Optional(8023),
> menu_item_title: Optional("Whiskey On the Rock"),
> menu_item_description: Optional("<p>Balvenie Whiskey On the
> Rock</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/whiskey-on-the-rock-1.jpg"),
> menu_item_price: Optional("8"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Appetizers"), menu_food_id: Optional(6486), menu_item_title:
> Optional("Pizza"), menu_item_description: Optional("<p>Pienapple
> Pizza</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2019/12/pizzaimage.jpe"),
> menu_item_price: Optional("10"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Appetizers"), menu_food_id: Optional(6489), menu_item_title:
> Optional("Sushi"), menu_item_description: Optional("<p>Generic
> Sushi</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2019/12/sushi.jpe"),
> menu_item_price: Optional("7"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Desserts"), menu_food_id: Optional(8021), menu_item_title:
> Optional("Ice Cream"), menu_item_description: Optional("<p>Served with
> hot fudge whipped cream and a cherry</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/ice-ice-cream-ice-cream-sundae-dessert-strawberries-berries-1.jpg"),
> menu_item_price: Optional("7.00"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Desserts"), menu_food_id: Optional(8026), menu_item_title:
> Optional("Slice of Cake"), menu_item_description:
> Optional("<p>Frosted</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/yummy-birthday-cake-on-platter-1-scaled.jpg"),
> menu_item_price: Optional("5.00"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Main DIshes"), menu_food_id: Optional(8015),
> menu_item_title: Optional("Salmon"), menu_item_description:
> Optional("<p>Served with rice</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/download-1.jpe"),
> menu_item_price: Optional("22.00"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Main DIshes"), menu_food_id: Optional(8013),
> menu_item_title: Optional("Steaks 10o"), menu_item_description:
> Optional("<p>Steaks 10O</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/testkitchen_argentinesteak-1.jpg"),
> menu_item_price: Optional("20.00"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Sides"), menu_food_id: Optional(8017), menu_item_title:
> Optional("Fries"), menu_item_description: Optional("<p>Salted
> Fries</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/Crispy-Oven-Fries-SpendWithPennies-27-480x270-1.jpg"),
> menu_item_price: Optional("9"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0), menu_item_result_multiple_string:
> Optional([""]), menu_item_description: Optional(""), price_update:
> Optional([""]))])), Carte.menu(menu_id: Optional(0), menu_category:
> Optional("Sides"), menu_food_id: Optional(8019), menu_item_title:
> Optional("Onion Rings"), menu_item_description: Optional("<p>Stacked
> in a tower</p>\n"), menu_item_imagefilePath:
> Optional("https://carteapp.net/wp-content/uploads/2020/07/4566354955_88c4163841_b-1.jpg"),
> menu_item_price: Optional("5.00"), menu_item_sale: Optional(""),
> menu_item_options:
> Optional([Carte.menu_item_options(menu_item_options_unique_id:
> Optional(0), menu_item_options_id: Optional(0),
> menu_item_options_title: Optional(""),
> menu_item_options_multiple_choice_title: Optional(""),
> menu_item_options_multiple_choice_label: Optional([""]),
> menu_item_result: Optional(0)

Categories looks like this, The String here is the Categories and the int here is where its positioned at on the table.

["Accessories": 0, "Appetizers": 1, "Main DIshes": 5, "Sides": 7, "Desserts": 3]

The code is here

 override func viewDidLoad() {
        super.viewDidLoad()
        filterMenuFunc()
        var count = 0
        while(filteredCategorizedMenu.count > count)
        {
            print(count)
            if (Categories == [:])
            {
                Categories = [filteredCategorizedMenu[count].menu_category : count] as! [String : Int]
                currentCategoryName = filteredCategorizedMenu[count].menu_category!
                arrayOfButtons.append(CategoryButton(key: filteredCategorizedMenu[count].menu_category!, Row: 0))
                print(Categories)
            }
            else if (filteredCategorizedMenu[count].menu_category != currentCategoryName)
            {
                print(filteredCategorizedMenu[count].menu_category! + " and " + currentCategoryName)
                Categories[filteredCategorizedMenu[count].menu_category!] = count
                currentCategoryName = filteredCategorizedMenu[count].menu_category!
                arrayOfButtons.append(CategoryButton(key: filteredCategorizedMenu[count].menu_category!, Row: count))
                print(Categories)
            }
            count = count + 1
        }
        
        let stackView = UIStackView(arrangedSubviews: arrayOfButtons)
        stackView.axis = .horizontal
        stackView.spacing = 20
        stackView.distribution = .fillProportionally
        stackView.translatesAutoresizingMaskIntoConstraints = false
        categoryView.addSubview(stackView)
        print(arrayOfButtons)
    }

func CategoryButton(key: String, Row: Int) -> UIButton
    {
        let Button = UIButton()
        Button.tag = Row
        Button.addTarget(self, action: Selector(("buttonCLicked")), for: .touchUpInside )
        Button.titleLabel!.textColor = .black
        Button.titleLabel!.text = key
        Button.titleLabel!.font = UIFont(name: "Avenir-Book", size: 20 )!
        Button.frame = CGRect(x: 0, y: 0, width: Button.intrinsicContentSize.width + 18, height: 40)

        return Button
    }
    
    func buttonClicked( _ sender: UIButton!)
    {
        let indexPath = IndexPath(row: sender!.tag, section: 0)
        menuTableView.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.top, animated: true)
    }

Button print looks like this

[<UIButton: 0x7fbce7d15f70; frame = (0 0; 48 40); opaque = NO; layer = <CALayer: 0x6000038014c0>>, <UIButton: 0x7fbce7d0aec0; frame = (0 0; 48 40); opaque = NO; tag = 1; layer = <CALayer: 0x600003802100>>, <UIButton: 0x7fbce7d150b0; frame = (0 0; 48 40); opaque = NO; tag = 3; layer = <CALayer: 0x600003802680>>, <UIButton: 0x7fbce7d21380; frame = (0 0; 48 40); opaque = NO; tag = 5; layer = <CALayer: 0x600003803460>>, <UIButton: 0x7fbce7d219a0; frame = (0 0; 48 40); opaque = NO; tag = 7; layer = <CALayer: 0x6000038030a0>>]
6
  • Not really seeing a specific question here. Try to re-think what you are asking. Have you tried to implement it? If so, what's not working the way you want? Commented Jul 9, 2020 at 14:40
  • Hi donmag, the question is what is the appropriate way to add buttons to a stackview with appropriate onclick listeners and display them. My use case is to have the amount of buttons equal to each unique categories. The categories name and amount of categories can fluctuate depending on the API request Commented Jul 9, 2020 at 18:53
  • So... you want to take your list of categories ["Accessories": 0, "Appetizers": 1, "Main DIshes": 5, "Sides": 7, "Desserts": 3] and create a button for each in a horizontal stack view in your categoryView, and then have a button tap function that where you can determine which button was tapped? Commented Jul 9, 2020 at 19:22
  • @DonMag, in short, yes :) Commented Jul 9, 2020 at 19:26
  • Is the number following each Category name supposed to be the number of items in that category? Commented Jul 9, 2020 at 20:04

1 Answer 1

1

Couple things...

You have several mistakes in the way you are creating your buttons. Also, if you're adding them to a stack view, don't bother setting their frames (that will be overridden by the stack view anyway).

You might be better off using multiple sections for your table view.

The buttons will be too wide to fit, so you probably want to put the stack view into a scroll view.

Anyway, it's not clear how you're parsing and storing your data, so I just mocked up an example. This assumes you have your green @IBOutlet weak var categoryView: UIView! already setup:

struct Category {
    var title: String = ""
    var rowNumber: Int = 0
}
class CategoryTestViewController: UIViewController {
    @IBOutlet var categoryView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // whatever your process is to build this array
        let myData = ["Accessories": 0, "Appetizers": 1, "Main DIshes": 5, "Sides": 7, "Desserts": 3]
        
        let categoryData: [Category] = [
            Category(title: "Accessories", rowNumber: 0),
            Category(title: "Appetizers", rowNumber: 1),
            Category(title: "Main Dishes", rowNumber: 5),
            Category(title: "Sides", rowNumber: 7),
            Category(title: "Desserts", rowNumber: 3),
            ]

        // create a horizontal stack view
        let buttonsStack = UIStackView()
        buttonsStack.translatesAutoresizingMaskIntoConstraints = false
        buttonsStack.axis = .horizontal
        buttonsStack.alignment = .fill
        buttonsStack.distribution = .fill
        buttonsStack.spacing = 20
        
        // for each category, add a button to the stack view
        categoryData.forEach { cat in
            let btn = CategoryButton(key: cat.title, Row: cat.rowNumber)
            buttonsStack.addArrangedSubview(btn)
        }
        
        // create a scroll view
        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        
        // add the buttons stack view to the scroll view
        scrollView.addSubview(buttonsStack)
        
        // add the scroll view to the "categoryView"
        categoryView.addSubview(scrollView)
        
        NSLayoutConstraint.activate([
            
            // constrain scrollView to all 4 sides
            scrollView.topAnchor.constraint(equalTo: categoryView.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: categoryView.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: categoryView.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: categoryView.bottomAnchor),

            // constrain stack view Top and Bottom
            //  Leading and Trailing with 20-pts "padding"
            buttonsStack.topAnchor.constraint(equalTo: scrollView.topAnchor),
            buttonsStack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 20),
            buttonsStack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -20),
            buttonsStack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            
            // constrain stack view Height to scrollView height
            buttonsStack.heightAnchor.constraint(equalTo: scrollView.heightAnchor),
            
        ])
    }
    
    func CategoryButton(key: String, Row: Int) -> UIButton
    {
        let Button = UIButton()
        Button.tag = Row

        Button.addTarget(self, action: #selector(self.buttonClicked(_:)), for: .touchUpInside )

        // don't do this
        //Button.titleLabel!.textColor = .black
        //Button.titleLabel!.text = key
        //Button.titleLabel!.font = UIFont(name: "Avenir-Book", size: 20 )!
        
        // don't set a frame
        //Button.frame = CGRect(x: 0, y: 0, width: Button.intrinsicContentSize.width + 18, height: 40)

        // create the button properly!
        Button.setTitle(key, for: [])
        Button.setTitleColor(.blue, for: [])
        Button.setTitleColor(.lightGray, for: .highlighted)
        Button.titleLabel?.font = UIFont(name: "Avenir-Book", size: 20 )
        
        return Button
    }

    @objc func buttonClicked( _ sender: UIButton!)
    {
        print("button \(sender.currentTitle) tapped")
        let indexPath = IndexPath(row: sender.tag, section: 0)
        //menuTableView.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.top, animated: true)
    }
    
}
Sign up to request clarification or add additional context in comments.

1 Comment

Your a god! thank you so much, I have learnt a lot from this code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.