swiftuikituisegmentedcontrol

removing the highlighted selected segment


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:

one two


Solution

  • 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:

    enter image description here

    Note that it will also work with more than 2 segments.