0
votes

I'm in the process of creating my own custom segmented controller. I'm following this video (https://www.youtube.com/watch?v=xGdRCUrSu94). Here is the code for the custom component:

//
//  YearSelectorSegControl.swift
//  OurDailyStrength iOS
//
//  Created by Rob Avery on 8/28/17.
//  Copyright © 2017 Rob Avery. All rights reserved.
//

import UIKit

@IBDesignable
class YearSelectorSegControl: UIView {

    var buttons = [UIButton]()
    var selector: UIView!
    var sv: UIStackView!

    @IBInspectable
    var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable
    var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }

    @IBInspectable
    var commaSeparatedButtonTitles: String = "" {
        didSet {
            updateView()
        }
    }

    @IBInspectable
    var textColor: UIColor = .lightGray {
        didSet {
            updateView()
        }
    }

    @IBInspectable
    var selectorColor: UIColor = .darkGray {
        didSet {
            updateView()
        }
    }

    @IBInspectable
    var selectorTextColor: UIColor = .white {
        didSet {
            updateView()
        }
    }

    func updateView() {
        // clear previous history
        buttons.removeAll()
        subviews.forEach { (view) in
            view.removeFromSuperview()
        }

        let buttonTitles = commaSeparatedButtonTitles.components(separatedBy: ",")

        // get names for buttons
        for buttonTitle in buttonTitles {
            let button = UIButton(type: .system)
            button.setTitle(buttonTitle, for: .normal)
            button.setTitleColor(textColor, for: .normal)
            button.addTarget(self, action: #selector(buttonTapped(clickedBtn:)), for: .touchUpInside)
            buttons.append(button)
        }

        buttons[0].setTitleColor(selectorTextColor, for: .normal)

        // configure selector (widget that highlights the selected item)
        let selectorWidth = frame.width / CGFloat(buttonTitles.count) // length given for selector
        selector = UIView(frame: CGRect(x: 0, y: 0, width: selectorWidth, height: frame.height))
        selector.layer.cornerRadius = 0
        selector.backgroundColor = selectorColor
        addSubview(selector)

        // add buttons to stackview
        sv = UIStackView(arrangedSubviews: buttons)
        sv.axis = .horizontal
        sv.alignment = .fill
        sv.distribution = .fillProportionally
        addSubview(sv)

        // set constraints for stackview
        sv.translatesAutoresizingMaskIntoConstraints = false
        sv.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        sv.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        sv.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
        sv.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
    }

    override func draw(_ rect: CGRect) {
        layer.cornerRadius = 0
    }

    func buttonTapped(clickedBtn: UIButton) {

        for (index, button) in buttons.enumerated() {
            if button == clickedBtn {
                let selectorStartPosition = frame.width/CGFloat(buttons.count) * CGFloat(index)
                UIView.animate(withDuration: 0.3, animations: {
                    self.selector.frame.origin.x = selectorStartPosition
                })
                button.setTitleColor(selectorTextColor, for: .normal)
            } else {
                button.setTitleColor(textColor, for: .normal)
            }
        }
    }

}

In the Storyboard, after it renders and draws the component, it shows this:

StoryBoard screenshot

This seems normal. You should have the ability, when you click on any other year buttons, that orange selector will move to that year. (This is simuliar to the segmented control).

When I run it, the simulator gives this:

Sim screenshot

This seems a little odd. The ornage selector looks a little wider than the one in the storybaord. So, I clicked on the last button ("2020"), and this is the result:

enter image description here

Ah, I see the selector seems to be getting the wrong width it needs. Within the code, you can see that it's getting the right width, but for some reason, it's not.

Why is it getting the wrong width? Am I using the wrong variables to calculate the right width?

1
I wouldn't combine auto layout and frame manipulation. You should do everything with constraints.Paulw11

1 Answers

0
votes

Why are you adding views to highlight the buttons? You should be configuring the buttons' selected state. You can set the tint color to set the selected background color, and set the title color of the button for the selected state.