Creating a custom segmented Control in swift

Leela Krishna
4 min readFeb 19, 2018

Hi every one, It’s my first story, to share with you on ios segmented control. The default one in ios, comes with the borders and customising that is not much easy. where now a days, some apps are being designed to look the same on Android and IOS, we need to customise many of our components, to give the user same experience even he changes the device(IOS← →Android). So, let’s create a segmented control which looks like a tabbed bar in Android which doesn’t have borders. This can be used as a slide menu or slider tabs like in XLPagerTabStrip or Android like tab bar with scroll. I createdThis Custom Segmented Control by watching Mark Tutorial youtube Channel, and customised more as per my requirement. let dive into code…

  1. We all know, UIControl is Inherited from UIView. All IOS or Xcode UIComponents are inherited from UIControl, Which has encapsulated or binded with all actions and listeners for our components.(UIControlEvents like .tpuchUpInside,.changeValue, etc.)

So, create a class which is subclass of UIControl, for the customSegmentedControl.

@IBDesignable class CustomSegmentedContrl: UIControl {

}

2. Now, add buttons array, which act like segments. To highlights the selected button/ Segment, we can use a separate view. Here I call it as a “selector”. I also put a underliner View at the bottom of the segmented control

var buttons = [UIButton]()

var underLiner: UIView!

var selector: UIView!

3. Here I give an option, to choose the Underliner with a flag.

var isUnderLinerNeeded: Bool = false {

didSet{

updateView()

}

}

4. Here, Two more properties which can be used for the segmented control

var selectedSegmentIndex = 0 {

didSet {

updateSegmentedControlSegs(index: selectedSegmentIndex)

}

}

var numberOfSegments: Int = 0 {

didSet {

numberOfSegments = buttons.count

}

}

5. Here, added All the @IBInspectables that needed for the Creation Of Our Custom Segmented Control

@IBInspectable var borderWidth: CGFloat = 0 {

didSet {

layer.borderWidth = borderWidth

}

}

@IBInspectable var cornerRadius: CGFloat = 0 {

didSet {

layer.cornerRadius = cornerRadius

}

}

@IBInspectable var borderColor: UIColor = .clear {

didSet {

layer.borderColor = borderColor.cgColor

}

}

@IBInspectable var commaSeperatedButtonTitles: String = “” {

didSet {

updateView()

}

}

@IBInspectable var textColor: UIColor = .lightGray {

didSet {

updateView()

}

}

@IBInspectable var selectorColor: UIColor = .darkGray {

didSet {

updateView()

}

}

@IBInspectable var selectorTextColor: UIColor = .green {

didSet {

updateView()

}

}

6. Here, I added The init & Required inits

override init(frame: CGRect) {

super.init(frame: frame)

updateView()

}

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

fatalError(“init(coder:) has not been implemented”)

}

7. Here, I called the function “updateView”, On selection of all those IBInspectables and other properties.

  • > In this Method, For every time calling this method we remove all the buttons. else, it will add those buttons again and again, every time the method calls.
  • > we get the button titles, from “commaSeperatedButtonTitles”, which is also an IBInspectable Property. all titles need to be passed by seperating a comma. For ex{“title1,title2,title3”}
  • > After Getting button titles, we extract them into an array, then we create buttons for each title and added them into a stack view. Here, we also added a selector View, Which highlights when a segmented is selected. underliner View is set if user wants it by accessing the flag property.
  • > When a segment/Button here is selected, we added a “addTarget Method” known as “ @objc func buttonTapped(button: UIButton) {}” as a selector, to update the selector position and update the selected button content.

func updateView() {

buttons.removeAll()

subviews.forEach { (view) in

view.removeFromSuperview()

}

let buttonTitles = commaSeperatedButtonTitles.components(separatedBy: “,”)

for buttonTitle in buttonTitles {

let button = UIButton.init(type: .system)

button.setTitle(buttonTitle, for: .normal)

button.titleLabel?.font = UIFont.init(name: “System-Bold”, size: 18)

button.setTitleColor(textColor, for: .normal)

button.addTarget(self, action: #selector(buttonTapped(button:)), for: .touchUpInside)

buttons.append(button)

// button.setTitleColor(button.isSelected ? UIColor.gray : selectorTextColor, for: .normal)

}

numberOfSegments = buttons.count

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

// For UnderLiner For SegmentedController

if isUnderLinerNeeded {

underLiner = UIView.init()

underLiner.backgroundColor = unSelectedColor

addSubview(underLiner)

underLiner.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([

underLiner.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),

underLiner.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),

underLiner.heightAnchor.constraint(equalToConstant: 2.0),

underLiner.topAnchor.constraint(equalTo: self.bottomAnchor, constant: -2.0)

])

}

let selectorWidth = frame.width / CGFloat(buttonTitles.count)

let y = (self.frame.maxY — self.frame.minY) — 3.0

selector = UIView.init(frame: CGRect.init(x: 0, y: y, width: selectorWidth, height: 3.0))

// selector.layer.cornerRadius = frame.height/2

selector.backgroundColor = selectorColor

addSubview(selector)

// Create a StackView

let stackView = UIStackView.init(arrangedSubviews: buttons)

stackView.axis = .horizontal

stackView.alignment = .fill

stackView.distribution = .fillEqually

stackView.spacing = 0.0

addSubview(stackView)

stackView.translatesAutoresizingMaskIntoConstraints = false

stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true

stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true

stackView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true

stackView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true

}

8. Here, The remaining code for the actions and Update Segmented control with change of segments(Synchronizing with IBActions)

  • > “ func updateSegmentedControlSegs(index: Int) {}” is a helper method to change the segments by passing an index parameter/argument. it will be useful when using UIPageViewController, or any other views in accordance with the segmented control.
  • -> Finally, at the last we Override the sendActions method for controlEvents, to send actions.

@objc func buttonTapped(button: UIButton) {

var selectorStartPosition: CGFloat!

for (buttonIndex,btn) in buttons.enumerated() {

btn.setTitleColor(textColor, for: .normal)

if btn == button {

selectedSegmentIndex = buttonIndex

selectorStartPosition = frame.width / CGFloat(buttons.count) * CGFloat(buttonIndex)

UIView.animate(withDuration: 0.3, animations: {

self.selector.frame.origin.x = selectorStartPosition

})

btn.setTitleColor(selectorTextColor, for: .normal)

}

}

sendActions(for: .valueChanged)

}

func updateSegmentedControlSegs(index: Int) {

var selectorStartPosition: CGFloat!

for btn in buttons {

btn.setTitleColor(textColor, for: .normal)

}

selectorStartPosition = frame.width / CGFloat(buttons.count) * CGFloat(index)

UIView.animate(withDuration: 0.3, animations: {

self.selector.frame.origin.x = selectorStartPosition

})

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

}

override func sendActions(for controlEvents: UIControlEvents) {

super.sendActions(for: controlEvents)

var selectorStartPosition: CGFloat!

selectorStartPosition = frame.width / CGFloat(buttons.count) * CGFloat(selectedSegmentIndex)

UIView.animate(withDuration: 0.3, animations: {

self.selector.frame.origin.x = selectorStartPosition

})

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

}

9. Finally to create your custom segmented control:

func createSegmentedControl() {

segmentedControl = CustomSegmentedContrl.init(frame: CGRect.init(x: 0, y: self.topView.frame.maxY+10, width: self.view.frame.width, height: 45))

// segmentedControl

segmentedControl.backgroundColor = .white

segmentedControl.commaSeperatedButtonTitles = “Title1,Title2,Title3”

segmentedControl.addTarget(self, action: #selector(onChangeOfSegment(_:)), for: .valueChanged)

segmentedControl.selectedSegmentIndex = 0

segmentedControl.textColor = UIColor.red

segmentedControl.selectorTextColor = UIColor.blue!

segmentedControl.isUnderLinerNeeded = true

self.view.addSubview(segmentedControl)

}

The Class file link is here.

Thanks for reading!!!

--

--