Skip to content

Instantly share code, notes, and snippets.

@kfarst
Created September 14, 2017 19:15
Show Gist options
  • Save kfarst/9f8a1eb59cce2004b15f0b682c92eeed to your computer and use it in GitHub Desktop.
Save kfarst/9f8a1eb59cce2004b15f0b682c92eeed to your computer and use it in GitHub Desktop.
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class Responder: NSObject {
@objc func segmentedControlValueChanged(_ sender: UISegmentedControl) {
UIView.animate(withDuration: 0.3) {
buttonBar.frame.origin.x = (segmentedControl.frame.width / CGFloat(segmentedControl.numberOfSegments)) * CGFloat(segmentedControl.selectedSegmentIndex)
}
}
}
let responder = Responder()
// Container view
let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 100))
view.backgroundColor = .white
let segmentedControl = UISegmentedControl()
// Add segments
segmentedControl.insertSegment(withTitle: "One", at: 0, animated: true)
segmentedControl.insertSegment(withTitle: "Two", at: 1, animated: true)
segmentedControl.insertSegment(withTitle: "Three", at: 2, animated: true)
// First segment is selected by default
segmentedControl.selectedSegmentIndex = 0
segmentedControl.backgroundColor = .clear
segmentedControl.tintColor = .clear
segmentedControl.setTitleTextAttributes([
NSAttributedStringKey.font : UIFont(name: "DINCondensed-Bold", size: 18),
NSAttributedStringKey.foregroundColor: UIColor.lightGray
], for: .normal)
segmentedControl.setTitleTextAttributes([
NSAttributedStringKey.font : UIFont(name: "DINCondensed-Bold", size: 18),
NSAttributedStringKey.foregroundColor: UIColor.orange
], for: .selected)
// This needs to be false since we are using auto layout constraints
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
let buttonBar = UIView()
// This needs to be false since we are using auto layout constraints
buttonBar.translatesAutoresizingMaskIntoConstraints = false
buttonBar.backgroundColor = UIColor.orange
// Add the segmented control to the container view
view.addSubview(segmentedControl)
view.addSubview(buttonBar)
// Constrain the segmented control to the top of the container view
segmentedControl.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
// Constrain the segmented control width to be equal to the container view width
segmentedControl.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
// Constrain the height of the segmented control to an arbitrarily chosen value
segmentedControl.heightAnchor.constraint(equalToConstant: 40).isActive = true
// Constrain the top of the button bar to the bottom of the segmented control
buttonBar.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor).isActive = true
buttonBar.heightAnchor.constraint(equalToConstant: 5).isActive = true
// Constrain the button bar to the left side of the segmented control
buttonBar.leftAnchor.constraint(equalTo: segmentedControl.leftAnchor).isActive = true
// Constrain the button bar to the width of the segmented control divided by the number of segments
buttonBar.widthAnchor.constraint(equalTo: segmentedControl.widthAnchor, multiplier: 1 / CGFloat(segmentedControl.numberOfSegments)).isActive = true
segmentedControl.addTarget(responder, action: #selector(responder.segmentedControlValueChanged(_:)), for: UIControlEvents.valueChanged)
PlaygroundPage.current.liveView = view
@hazelf42
Copy link

Hello! If we're doing this within an app, do we want all the code to be in ViewDidLoad? I keep getting errors because of the @objc code, which, if taken out of ViewDidLoad, stops working

(I'm new at this, can you tell?)

@tonespy
Copy link

tonespy commented Jan 15, 2019

FuturePriceDaysSegment

import UIKit

class FuturePriceDaysSegment: UIView {
    
    let daysSegmentControl: UISegmentedControl = {
        let control = UISegmentedControl()
        control.backgroundColor = .clear
        control.tintColor = .clear
        control.setTitleTextAttributes([
            NSAttributedString.Key.font: UIFont.mySFMedium(ofSize: 14) as Any,
            NSAttributedString.Key.foregroundColor: UIColor(red:0.36, green:0.38, blue:0.44, alpha:1)
            ], for: .normal)
        control.setTitleTextAttributes([
            NSAttributedString.Key.font: UIFont.mySFMedium(ofSize: 14) as Any,
            NSAttributedString.Key.foregroundColor: UIColor(red:0.12, green:0.49, blue:0.86, alpha:1)
            ], for: .selected)
        control.translatesAutoresizingMaskIntoConstraints = false
        return control
    }()
    
    let bottomBar: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor(red:0.12, green:0.49, blue:0.86, alpha:1)
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    var segmentItems: [String] = ["7 days", "30 days", "90 days", "1 year"] {
        didSet {
            guard segmentItems.count > 0 else { return }
            setupSegmentItems()
            bottomBarWidthAnchor?.isActive = false
            bottomBarWidthAnchor = bottomBar.widthAnchor.constraint(equalTo: daysSegmentControl.widthAnchor, multiplier: 1 / CGFloat(segmentItems.count))
            bottomBarWidthAnchor?.isActive = true
        }
    }
    
    var bottomBarWidthAnchor: NSLayoutConstraint?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    
    private func setup() {
        addSubview(daysSegmentControl)
        addSubview(bottomBar)
        
        daysSegmentControl.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
        daysSegmentControl.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        daysSegmentControl.topAnchor.constraint(equalTo: topAnchor).isActive = true
        daysSegmentControl.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        daysSegmentControl.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged)
        
        bottomBar.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        bottomBar.heightAnchor.constraint(equalToConstant: 2).isActive = true
        bottomBar.leftAnchor.constraint(equalTo: daysSegmentControl.leftAnchor).isActive = true
        bottomBarWidthAnchor = bottomBar.widthAnchor.constraint(equalTo: daysSegmentControl.widthAnchor, multiplier: 1 / CGFloat(segmentItems.count))
        bottomBarWidthAnchor?.isActive = true
        
        setupSegmentItems()
    }
    
    private func setupSegmentItems() {
        for (index, value) in segmentItems.enumerated() {
            daysSegmentControl.insertSegment(withTitle: value, at: index, animated: true)
        }
        daysSegmentControl.selectedSegmentIndex = 0
    }
    
    @objc func segmentedControlValueChanged(_ sender: UISegmentedControl) {
        UIView.animate(withDuration: 0.3) {
            let originX = (self.daysSegmentControl.frame.width / CGFloat(self.segmentItems.count)) * CGFloat(self.daysSegmentControl.selectedSegmentIndex)
            self.bottomBar.frame.origin.x = originX
        }
    }
}

Implementation Sample

...
view.addSubview(sampleSegment)

sampleSegment.heightAnchor.constraint(equalToConstant: 45).isActive = true
sampleSegment.topAnchor.constraint(equalTo: someView.bottomAnchor, constant: 5).isActive = true
sampleSegment.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
sampleSegment.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

Check it out:

https://gph.is/2RwAIgq

@hazelf42

@sudhakarmallidi
Copy link

Thank you Abubakar Oladeji tonespy. Great Job.

@Sadmansamee
Copy link

Sadmansamee commented Apr 14, 2020

More Improved version,

  • tabSegmentedControlChanged Call back on Control Changed event,
  • Setting segmentItems from outside via func setSegmentItems
  • Set Selected index via setSelectedSegmentIndex
    `import Foundation
    import UIKit

class TabSegmentedControlView: UIView {

var tabSegmentedControlChanged: ((_ index: Int) -> Void)?


private let segmentControl: UISegmentedControl = {
    let control = UISegmentedControl()
    control.backgroundColor = .clear
    control.tintColor = .clear
    control.setTitleTextAttributes([
        NSAttributedString.Key.backgroundColor: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
        NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14,weight: .medium),
        NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.423489809, green: 0.4235547483, blue: 0.4234755933, alpha: 1)
        ], for: .normal)
    control.setTitleTextAttributes([
        NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14,weight: .medium),
        NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.877774775, green: 0.1001265123, blue: 0.2252686918, alpha: 1),
        NSAttributedString.Key.backgroundColor: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        ], for: .selected)
    control.translatesAutoresizingMaskIntoConstraints = false
    control.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    control.tintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    if #available(iOS 13.0, *) {
        control.selectedSegmentTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    }
    return control
}()

private let bottomBar: UIView = {
let view = UIView()
view.backgroundColor = #colorLiteral(red: 0.8819062114, green: 0.1067187563, blue: 0.2246125638, alpha: 1)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

private var segmentItems: [String] = ["title","title"] {
didSet {
guard !segmentItems.isEmpty else { return }
setupSegmentItems()
bottomBarWidthAnchor?.isActive = false
bottomBarWidthAnchor = bottomBar.widthAnchor.constraint(equalTo: segmentControl.widthAnchor, multiplier: 1 / CGFloat(segmentItems.count))
bottomBarWidthAnchor?.isActive = true
}
}
var bottomBarWidthAnchor: NSLayoutConstraint?

override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
}

private func setup() {
    addSubview(segmentControl)
    addSubview(bottomBar)

    segmentControl.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
    segmentControl.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
    segmentControl.topAnchor.constraint(equalTo: topAnchor).isActive = true
    segmentControl.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    segmentControl.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged)

    bottomBar.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    bottomBar.heightAnchor.constraint(equalToConstant: 2).isActive = true
    bottomBar.leftAnchor.constraint(equalTo: segmentControl.leftAnchor).isActive = true
    bottomBarWidthAnchor = bottomBar.widthAnchor.constraint(equalTo: segmentControl.widthAnchor, multiplier: 1 / CGFloat(segmentItems.count))
    bottomBarWidthAnchor?.isActive = true

    setupSegmentItems()
}

private func setupSegmentItems() {
    for (index, value) in segmentItems.enumerated() {
        segmentControl.insertSegment(withTitle: value, at: index, animated: true)
    }
    segmentControl.selectedSegmentIndex = 0
}

@objc private  func segmentedControlValueChanged(_ sender: UISegmentedControl) {
    UIView.animate(withDuration: 0.3) {
        let originX = (self.segmentControl.frame.width / CGFloat(self.segmentItems.count)) * CGFloat(self.segmentControl.selectedSegmentIndex)
        self.bottomBar.frame.origin.x = originX
    }
}

func setSelectedSegmentIndex(index: Int){
    segmentControl.selectedSegmentIndex = index
}

func setSegmentItems (titles : [String] ) {
    segmentControl.removeAllSegments()
    self.segmentItems = titles
}

}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment