-
-
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 |
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:
Thank you Abubakar Oladeji tonespy. Great Job.
More Improved version,
tabSegmentedControlChanged
Call back on Control Changed event,- Setting
segmentItems
from outside via funcsetSegmentItems
- 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
}
}
`
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?)