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:
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:
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:
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?