I made a custom UIControl:
import UIKit
@IBDesignable class FeedViewSC: UIControl {
fileprivate var labels = [UILabel]()
var thumbView = UIView()
var items: [String] = ["Tab1", "Tab2"] {
didSet {
setupLabels()
}
}
var selectedIndex : Int = 0 {
didSet{
displayNewSelectedIndex()
}
}
@IBInspectable var font : UIFont! = UIFont.systemFont(ofSize: 13) {
didSet {
setFont()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
func setupView() {
layer.cornerRadius = 5
layer.borderColor = UIColor(red: 239/255, green: 239/255, blue: 239/255, alpha: 1).cgColor
layer.borderWidth = 2
backgroundColor = UIColor(red: 239/255, green: 239/255, blue: 239/255, alpha: 1)
setupLabels()
insertSubview(thumbView, at: 0)
}
func setupLabels() {
for label in labels {
label.removeFromSuperview()
}
labels.removeAll(keepingCapacity: true)
for index in 1...items.count {
let label = UILabel(frame: CGRect.zero)
label.text = items[index-1]
label.textAlignment = .center
label.font = UIFont(name: "Nunito",size: 17)
label.textColor = UIColor(red: 51/255, green: 51/255, blue: 51/255, alpha: 1)
self.addSubview(label)
labels.append(label)
}
}
override func layoutSubviews() {
super.layoutSubviews()
var selectFrame = self.bounds
let newWidth = selectFrame.width / CGFloat(items.count)
selectFrame.size.width = newWidth
thumbView.frame = selectFrame
thumbView.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1)
thumbView.layer.cornerRadius = 5
let labelHeight = self.bounds.height
let labelWidth = self.bounds.width / CGFloat(labels.count)
for index in 0...labels.count - 1 {
let label = labels[index]
let xPosition = CGFloat(index) * labelWidth
label.frame = CGRect(x: xPosition, y: 0, width: labelWidth, height: labelHeight)
}
}
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let location = touch.location(in: self)
var calculatedIndex: Int?
for (index, item) in labels.enumerated() {
if item.frame.contains(location){
calculatedIndex = index
}
}
if calculatedIndex != nil {
selectedIndex = calculatedIndex!
sendActions(for: .valueChanged)
}
return false
}
func displayNewSelectedIndex (){
if(self.selectedIndex == -1){
self.selectedIndex = self.items.count-1
}
let label = labels[selectedIndex]
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.90, initialSpringVelocity: 0.8, options: [], animations: {
self.thumbView.frame = label.frame
}, completion: nil)
}
func setFont(){
for item in labels {
item.font = font
}
}
}
I emeded it into my ViewController and tried to have it programitcally switch between two views:
import UIKit
class FeedViewController: UIViewController {
let feedViewSC = FeedViewSC()
let firstView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let secondView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let (width, height) = UIScreen.main.bounds.wh
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
view.addSubview(firstView)
view.addSubview(secondView)
setupContainerViews()
firstView.isHidden = false
secondView.isHidden = true
myTravel.selectedIndex = 0
let feedControl = FeedViewSC(frame: CGRect(x: 65, y: 5, width: width-130, height: 35))
feedControl.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.navigationController?.navigationBar.addSubview(feedControl)
feedControl.addTarget(self, action: #selector(segmentedControlSwitch), for: .valueChanged)
}
func segmentedControlSwitch(_ sender: FeedViewSC) {
switch feedViewSC.selectedIndex {
case 0:
firstView.isHidden = false
secondView.isHidden = true
case 1:
firstView.isHidden = true
secondView.isHidden = false
default:
break;
}
}
func setupContainerViews() {
firstView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
firstView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 8).isActive = true
firstView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
firstView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
secondView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
secondView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 8).isActive = true
secondView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
secondView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
}
override func viewDidAppear(_ animated: Bool) {
let img = UIImage()
self.navigationController?.navigationBar.shadowImage = img
self.navigationController?.navigationBar.setBackgroundImage(img, for: UIBarMetrics.default)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension CGRect {
var wh: (w: CGFloat, h: CGFloat) {
return (size.width, size.height)
}
}
I'm not sure what I'm doing that's wrong. For some reason, the code does not switch between firstView and secondView. Currently, it is just showing the first view. please keep in mind i am doing this without storyboards and all programmatically.
The issue seems in segmentedControlSwitch method. You should be using sender in switch case instead of local variable which might not be initialized.
func segmentedControlSwitch(_ sender: myTripsSC) {
switch sender.selectedIndex {
case 0:
firstView.isHidden = false
secondView.isHidden = true
case 1:
firstView.isHidden = true
secondView.isHidden = false
default:
break;
}
}
Edit: your vc should look like this:
import UIKit
class FeedViewController: UIViewController {
let feedViewSC = FeedViewSC()
let firstView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let secondView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let (width, height) = UIScreen.main.bounds.wh
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
view.addSubview(firstView)
view.addSubview(secondView)
setupContainerViews()
firstView.isHidden = false
secondView.isHidden = true
//myTravel.selectedIndex = 0
let feedControl = FeedViewSC(frame: CGRect(x: 65, y: 5, width: width-130, height: 35))
feedControl.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.navigationController?.navigationBar.addSubview(feedControl)
feedControl.addTarget(self, action: #selector(segmentedControlSwitch), for: .valueChanged)
}
func segmentedControlSwitch(_ sender: FeedViewSC) {
switch sender.selectedIndex {
case 0:
firstView.isHidden = false
secondView.isHidden = true
case 1:
firstView.isHidden = true
secondView.isHidden = false
default:
break;
}
}
func setupContainerViews() {
if #available(iOS 9.0, *) {
firstView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
firstView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 8).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
firstView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
firstView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
secondView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
secondView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 8).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
secondView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
} else {
// Fallback on earlier versions
}
if #available(iOS 9.0, *) {
secondView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
} else {
// Fallback on earlier versions
}
}
override func viewDidAppear(_ animated: Bool) {
let img = UIImage()
self.navigationController?.navigationBar.shadowImage = img
self.navigationController?.navigationBar.setBackgroundImage(img, for: UIBarMetrics.default)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension CGRect {
var wh: (w: CGFloat, h: CGFloat) {
return (size.width, size.height)
}
}