can u guys tell me how to remove that highlight it has? i cant find a way to do it.
this is how it is working:
As I commented when you posted about this previously - heading over to Google (or your favorite search engine) and searching along the lines of swift custom uisegmentedcontrol
... returns many, many examples, discussions, tutorials, etc. You could almost certainly find a pre-built one.
It appears you want a segmented control with only an underline -- no borders or highlight or separator line at all.
However, here is a quick, very simple example that looks like what you're trying to do.
We'll create a custom view class, with an embedded UISegmentedControl
and an "underline" view. This gives us all the advantages of the UISegmentedControl
without messing with its internal view structure:
class MyCustomSegmentedControlView: UIView {
public var underlineColor: UIColor = .black {
didSet { underlineView.backgroundColor = underlineColor }
}
public let segCtrl = UISegmentedControl()
public func setSegmentTitles(_ titles: [String]) {
segCtrl.removeAllSegments()
titles.reversed().forEach { str in
segCtrl.insertSegment(withTitle: str, at: 0, animated: false)
}
segCtrl.selectedSegmentIndex = 0
moveUnderline(animated: false)
}
private let underlineView = UIView()
private var segCtrlCurrentSize: CGSize = .zero
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
segCtrl.translatesAutoresizingMaskIntoConstraints = false
addSubview(segCtrl)
addSubview(underlineView)
NSLayoutConstraint.activate([
segCtrl.topAnchor.constraint(equalTo: topAnchor),
segCtrl.leadingAnchor.constraint(equalTo: leadingAnchor),
segCtrl.trailingAnchor.constraint(equalTo: trailingAnchor),
segCtrl.bottomAnchor.constraint(equalTo: bottomAnchor),
])
underlineView.backgroundColor = underlineColor
// this will "remove" the highlight frame
// not really remove... just make it clear
segCtrl.selectedSegmentTintColor = .clear
segCtrl.addTarget(self, action: #selector(segChanged(_:)), for: .valueChanged)
}
@objc func segChanged(_ sender: UISegmentedControl) {
moveUnderline(animated: true)
}
private func moveUnderline(animated: Bool) {
// make sure selectedSegmentIndex is valid
// if not, hide the underline view
guard segCtrl.selectedSegmentIndex > -1,
segCtrl.selectedSegmentIndex < segCtrl.numberOfSegments
else {
underlineView.isHidden = true
return
}
underlineView.isHidden = false
// put the underline view at the bottom of the selected segment
// same width as the segment, 2-points height
var r = segCtrl.subviews[segCtrl.selectedSegmentIndex].frame
r.origin.y = r.height - 2.0
r.size.height = 2.0
if animated {
UIView.animate(withDuration: 0.3, animations: {
self.underlineView.frame = r
})
} else {
self.underlineView.frame = r
}
}
override func layoutSubviews() {
super.layoutSubviews()
// if the segmented control frame has changed
if segCtrlCurrentSize != segCtrl.frame.size {
segCtrlCurrentSize = segCtrl.frame.size
// background and divider images are stretched
// horizontally / vertically respectively
// so we can use a single 1 x controlHeight image for both
let sz: CGSize = .init(width: 1.0, height: segCtrlCurrentSize.height)
var img = UIGraphicsImageRenderer(size: sz).image { rendererContext in
UIColor.white.setFill()
rendererContext.fill(CGRect(origin: .zero, size: sz))
}
segCtrl.setBackgroundImage(img, for: [], barMetrics: .default)
segCtrl.setDividerImage(img, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
moveUnderline(animated: false)
}
}
}
and asimple example controller class:
class ViewController: UIViewController {
let mySeg = MyCustomSegmentedControlView()
override func viewDidLoad() {
super.viewDidLoad()
mySeg.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mySeg)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
mySeg.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
mySeg.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
mySeg.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// no height or bottom - it will use the segmented control's intrinsic height
])
mySeg.setSegmentTitles(["Cart", "Wishlist"])
mySeg.underlineColor = .orange
// we could use:
// protocol/delegate pattern, or
// closure
// for simplicity, we'll add another target to the segmented control
mySeg.segCtrl.addTarget(self, action: #selector(segChanged(_:)), for: .valueChanged)
}
@objc func segChanged(_ sender: UISegmentedControl) {
print("Seg Control Changed to:", sender.selectedSegmentIndex)
}
}
It looks like this:
Note that it will also work with more than 2 segments.