Happiehappie
Happiehappie

Reputation: 1074

How to remove border from segmented control

How do I remove the outside border of a segmented control? I've set the divider image to what I wanted but now to follow the mock of my app I need to have a segmented control without the outer border.

Upvotes: 22

Views: 32180

Answers (9)

jqgsninimo
jqgsninimo

Reputation: 7038

Sohil's answer did help me.

But if you don't set the UIImage size, it may affect the size of the UISegmentedControl.

If you also encounter this problem, please consider using the following code.

func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
    return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
}

extension UIImage {
    func resized(_ size: CGSize) -> UIImage? {
        defer { UIGraphicsEndImageContext() }
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        self.draw(in: CGRect(origin: .zero, size: size))
        return UIGraphicsGetImageFromCurrentImageContext()
    }

    func resized(_ size: CGFloat) -> UIImage? {
        guard size > 0, self.size.width > 0, self.size.height > 0 else { return nil }
        let ratio = size / max(self.size.width, self.size.height)
        return self.resized(self.size * ratio)
    }

    func refilled(_ color: UIColor) -> UIImage? {
        defer { UIGraphicsEndImageContext() }
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color.set()
        UIGraphicsGetCurrentContext()?.fill(CGRect(origin: .zero, size: self.size))
        return UIGraphicsGetImageFromCurrentImageContext()
    }

    public convenience init?(color: UIColor = .clear, size: CGSize) {
        defer { UIGraphicsEndImageContext() }
        UIGraphicsBeginImageContext(size)

        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        context.setFillColor(color.cgColor);
        context.fill(CGRect(origin: .zero, size: size));
        guard let cgImage = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { return nil }

        self.init(cgImage: cgImage)
    }
}

extension UISegmentedControl {
    func removeBorders() {
        let image = UIImage(size: self.bounds.size)
        self.setBackgroundImage(image, for: .normal, barMetrics: .default)
        self.setBackgroundImage(image?.refilled(self.tintColor), for: .selected, barMetrics: .default)
        self.setDividerImage(image?.resized(1), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
    }
}

Easy to use:

segmentedControl.removeBorders()

Upvotes: 0

Sohel L.
Sohel L.

Reputation: 9540

What you must understand is the backgroundColor property is not stateful. Hence you have to use setBackgroundImage(_:for:barMetrics:).

We can easily remove both borders and dividers using the below function.

For Swift 3 & 4+:

extension UISegmentedControl {
    func removeBorders() {
        setBackgroundImage(imageWithColor(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
        setBackgroundImage(imageWithColor(color: tintColor!), for: .selected, barMetrics: .default)
        setDividerImage(imageWithColor(color: UIColor.clear), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
    }

    // create a 1x1 image with this color
    private func imageWithColor(color: UIColor) -> UIImage {
        let rect = CGRect(x: 0.0, y: 0.0, width:  1.0, height: 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        context!.setFillColor(color.cgColor);
        context!.fill(rect);
        let image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image!
    }
}

For Swift 2.2:

extension UISegmentedControl {
    func removeBorders() {
        setBackgroundImage(imageWithColor(backgroundColor!), forState: .Normal, barMetrics: .Default)
        setBackgroundImage(imageWithColor(tintColor!), forState: .Selected, barMetrics: .Default)
        setDividerImage(imageWithColor(UIColor.clearColor()), forLeftSegmentState: .Normal, rightSegmentState: .Normal, barMetrics: .Default)
    }

    // create a 1x1 image with this color
    private func imageWithColor(color: UIColor) -> UIImage {
        let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        CGContextSetFillColorWithColor(context, color.CGColor);
        CGContextFillRect(context, rect);
        let image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image
    }
}

Call the above function.

segmentedControl.removeBorders()

Reference: Remove UISegmentedControl separators completely. (iphone)

Thanks to https://stackoverflow.com/users/3921490/amagain for Swift 3 version.

Upvotes: 72

Alessandro Ornano
Alessandro Ornano

Reputation: 35392

Swift 5.x:

This solution remove only the external border and preserve the round corner on each button

extension UISegmentedControl {
    func removeBorders(andBackground:Bool=false) {
        setBackgroundImage(imageWithColor(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
        setBackgroundImage(imageWithColor(color: tintColor!), for: .selected, barMetrics: .default)
        setDividerImage(imageWithColor(color: UIColor.clear), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)

        _ = self.subviews.compactMap {
            if ($0.frame.width>0) {
                $0.layer.cornerRadius = 8
                $0.layer.borderColor = UIColor.clear.cgColor
                $0.clipsToBounds = true
                $0.layer.borderWidth = andBackground ? 1.0 : 0.0
                $0.layer.borderColor = andBackground ? tintColor?.cgColor : UIColor.clear.cgColor
                andBackground ? $0.layer.backgroundColor = UIColor.clear.cgColor : nil
            }
        }
    }

    // create a 1x1 image with this color
    private func imageWithColor(color: UIColor) -> UIImage {
        let rect = CGRect(x: 0.0, y: 0.0, width:  1.0, height: 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        context!.setFillColor(color.cgColor);
        context!.fill(rect);
        let image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image!
    }
}

Usage:

mySegmentedControl.removeBorders()

Upvotes: 0

mfaani
mfaani

Reputation: 36287

  • The accepted answer works for getting rid of the border.
  • The solution mentioned here also works for disabling the segmented Control.

Yet putting the two solutions together, I wasn't able to get the correct selected segment in a disabled state.

What I wanted was that upon selecting a segment and making a network call, I wanted to disable the segmented control.

The outcome was like this:

enter image description here

What was desired was this:

enter image description here

The only addition to Sohil's solution is:

setBackgroundImage(imageWithColor(color: imageWithColor(color: tintColor)), for: [.selected, .disabled], barMetrics: .default)

After the selected && disabled segment will have the right color.

Upvotes: 0

Lal Krishna
Lal Krishna

Reputation: 16160

If you need segment control with texts Only, use,

segmentControl.backgroundColor = .clear
segmentControl.tintColor = .clear

let attributes: [NSAttributedString.Key : Any] = [.font : UIFont(family: .medium, ofSize: 13)!, .foregroundColor : UIColor.white]
segmentControl.setTitleTextAttributes(attributes, for: .normal)

let selectedAttrib: [NSAttributedString.Key : Any] = [.font : UIFont(family: .medium, ofSize: 13)!, .foregroundColor : UIColor.red]
segmentControl.setTitleTextAttributes(hightLightedStateAttrib, for: .selected)

NB: I googled it for a long time, thats why i'm posted here.

Source: CodeMentor

Upvotes: 0

AlexK
AlexK

Reputation: 396

You might have the usecase where you don't want the rounded border as you don't want to see it if it's embedded in a cell. In this case, set the autolayout constraints to -2 and the border will be hidden as it will extend beyond the cell border.

Upvotes: 0

kalpa
kalpa

Reputation: 982

Hope it will help someone

Swift - 4

Make the Background color and Tint color of your Segment control to same color. Then "set titleTextAttributes" of your Segment control

    segmentedControl.tintColor = UIColor.red
    segmentedControl.backgroundColor = UIColor.red
    let attributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
    segmentedControl.setTitleTextAttributes(attributes, for: .normal)
    segmentedControl.setTitleTextAttributes(attributes, for: .selected)

Upvotes: 6

miss Gbot
miss Gbot

Reputation: 373

If you want save borders between cells

extension UISegmentedControl {
  func removeBorders() {
    if let backgroundColor = backgroundColor, let backgroundImage = UIImage.imageWithSize(size: CGSize.one_one, color: backgroundColor){
      setBackgroundImage(backgroundImage, for: .normal, barMetrics: .default)
    }

    if let tintColor = tintColor, let tintImage = UIImage.imageWithSize(size: CGSize.one_one, color: tintColor){
      setBackgroundImage(tintImage, for: .selected, barMetrics: .default)
        setDividerImage(tintImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
    }
  }
}

extension CGSize{
  static var one_one: CGSize{
    return CGSize(width: 1.0, height: 1.0)
  }
}

extension UIImage{
  static func imageWithSize(size : CGSize, color : UIColor = UIColor.white) -> UIImage? {
    var image:UIImage? = nil
    UIGraphicsBeginImageContext(size)
    if let context = UIGraphicsGetCurrentContext() {
      context.setFillColor(color.cgColor)
      context.addRect(CGRect(origin: CGPoint.zero, size: size));
      context.drawPath(using: .fill)
      image = UIGraphicsGetImageFromCurrentImageContext();
    }
    UIGraphicsEndImageContext()
    return image
  }
}

Upvotes: 2

amagain
amagain

Reputation: 2072

Here's the swift 3 version of Sohil's answer that might help someone else. It did help me. :)

extension UISegmentedControl {
func removeBorders() {
    setBackgroundImage(imageWithColor(color: backgroundColor!), for: .normal, barMetrics: .default)
    setBackgroundImage(imageWithColor(color: tintColor!), for: .selected, barMetrics: .default)
    setDividerImage(imageWithColor(color: UIColor.clear), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
}

// create a 1x1 image with this color
private func imageWithColor(color: UIColor) -> UIImage {
    let rect = CGRect(x: 0.0, y: 0.0, width:  1.0, height: 1.0)
    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()
    context!.setFillColor(color.cgColor);
    context!.fill(rect);
    let image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image!
    }
}

Upvotes: 12

Related Questions