{"id":11483,"date":"2017-04-28T08:44:51","date_gmt":"2017-04-28T08:44:51","guid":{"rendered":"https:\/\/sep.live.e3cms.net\/blog\/custom-uisegmentedcontrol-swift-tutorial\/"},"modified":"2017-04-28T08:44:51","modified_gmt":"2017-04-28T08:44:51","slug":"custom-uisegmentedcontrol-swift-tutorial","status":"publish","type":"post","link":"https:\/\/hexboxdev.wpenginepowered.com\/blog\/custom-uisegmentedcontrol-swift-tutorial\/","title":{"rendered":"Swift iOS Tutorial: How to Customize UISegmentedControl"},"content":{"rendered":"<p><!--kg-card-begin: markdown--><\/p>\n<h2 id=\"customizingauisegmentedcontrol\"><strong>Customizing a UISegmentedControl<\/strong><\/h2>\n<p>If you have spent any time developing an iOS app, you have probably realized that customizing UI elements in Swift can be hard. There is either a checkbox that does what you want, or it takes hours of research and trial and error to get something just the way you want it. For example, customizing a UISegmentedControl to display the selected segment as white is pretty straightforward. Apple provides a place to make this change in storyboard or you can change the tintColor property in code. \u00a0However, changing the selected segment to have a darker background while the text and border remain white as in the image below is not so trivial.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2020\/12\/2017-04-Screen-Shot-2017-04-18-at-11.30.29-AM.png\" alt=\"Custom UISegmentedControl example\" \/><\/p>\n<p>This Swift iOS tutorial will help you get a segmented control that behaves as the one above. \u00a0As a disclaimer, the gradient background is just an image behind the UISegmentedControl, and the segments and their titles are set up in storyboard. This tutorial is primarily focused on customizing the look of the selected segment.<\/p>\n<h2 id=\"thetutorialcustomsegmentedcontrol\"><strong>The Tutorial: CustomSegmentedControl<\/strong><\/h2>\n<p>Add a new file to your project called CustomSegmentedControl.swift and add the following code to it:<\/p>\n<pre><code class=\"language-java\">import UIKit class CustomSegmentedControl: UISegmentedControl {     required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        configure()     }     private func configure() {    }    func changeSelectedIndex(to newIndex: Int) {    }}<\/code><\/pre>\n<h3 id=\"identifyingtheproblem\"><strong>Identifying the Problem<\/strong><\/h3>\n<p>The inherent problem with trying to get the selected segment\u2019s background to change independently of everything else is that by default, the selected segment changes its background to fill with\u00a0the UISegmentedControl\u2019s tint color and changes its text color to be clear. \u00a0Any attempts to change the background color of the segment will be overwritten by this default behavior as soon as it is selected. So we need to work around this behavior.<\/p>\n<p><strong>Important<\/strong>: First, set up an IBAction outlet for the UISegmentedControl\u2019s ValueChanged and make sure that sender is a CustomSegmentedControl.\u00a0 In that function, call sender.changeSelectedIndex(to: sender.selectedSegmentIndex).<\/p>\n<pre><code class=\"language-java\">@IBAction func segmentControlValueChanged(_ sender: CustomSegmentedControl) {    sender.changeSelectedIndex(to: sender.selectedSegmentIndex)}<\/code><\/pre>\n<p>Because we cannot change the default behavior of the selected segment, the first thing we have to do is to make it so that the selected segment isn\u2019t actually selected.\u00a0 Add a currentIndex variable to manually keep track of which segment is selected.\u00a0 In changeSelectedIndex, set the currentIndex to newIndex and set the UISegmentControl\u2019s selectedSegmentIndex to UISegmentedControlNoSegment. This will deselect the selected segment and now we can begin to customize the\u00a0UISegmentcontrol. This is the current state of the file:<\/p>\n<pre><code class=\"language-java\">import UIKitclass CustomSegmentedControl: UISegmentedControl {    var currentIndex: Int = 0 \u00a0     required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        configure()    }    private func configure() {    }    func changeSelectedIndex(to newIndex: Int) {        currentIndex = newIndex        self.selectedSegmentIndex = UISegmentedControlNoSegment    }}<\/code><\/pre>\n<h3 id=\"solvingtheproblem\"><strong>Solving the Problem<\/strong><\/h3>\n<p>The only way to change properties of the views in a UISegmentedControl is to access its list of subviews. This list, however, is not guaranteed to be in any particular order.\u00a0 Because of this, add a variable to the\u00a0class called sortedViews that can be used to access the subviews of the UISegmentedControl.\u00a0 In configure, sort the array of subviews by their x position and save it in the\u00a0new variable.<\/p>\n<pre><code class=\"language-java\">import UIKitclass CustomSegmentedControl: UISegmentedControl {    var sortedViews: [UIView]!    var currentIndex: Int = 0    required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        configure()    }    private func configure() {        sortedViews = self.subviews.sorted(by:{$0.frame.origin.x &lt; $1.frame.origin.x})    }    func changeSelectedIndex(to newIndex: Int) {        currentIndex = newIndex        self.selectedSegmentIndex = UISegmentedControlNoSegment    }}<\/code><\/pre>\n<h3 id=\"makingitlookgood\"><strong>Making it Look Good<\/strong><\/h3>\n<p>Now we have all of the tools in place to customize the\u00a0UISegmentedControl. \u00a0Define a background color for the\u00a0selected segment as selectedBackgroundColor. \u00a0In changeSelectedIndex, set the old selectedIndex\u2019s background color to clear and set\u00a0the new selected segment\u2019s\u00a0background color to the defined color. \u00a0Also add a call to changeSelectedIndex in configure to initialize our UISegmentedControl properly.<\/p>\n<pre><code class=\"language-java\">import UIKitclass CustomSegmentedControl: UISegmentedControl {        let selectedBackgroundColor = UIColor(red: 19\/255, green: 59\/255, blue: 85\/255, alpha: 0.5)    var sortedViews: [UIView]!    var currentIndex: Int = 0        required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        configure()    }       private func configure() {        sortedViews = self.subviews.sorted(by:{$0.frame.origin.x &lt; $1.frame.origin.x})        changeSelectedIndex(to: currentIndex)    }        func changeSelectedIndex(to newIndex: Int) {        sortedViews[currentIndex].backgroundColor = UIColor.clear        currentIndex = newIndex        self.selectedSegmentIndex = UISegmentedControlNoSegment        sortedViews[currentIndex].backgroundColor = selectedBackgroundColor    }}<\/code><\/pre>\n<h3 id=\"finishingtouches\"><strong>Finishing Touches<\/strong><\/h3>\n<p>The last step is to add a few finishing touches to the\u00a0configure function including the tint color, rounded corners, and font styling. This is the completed file:<\/p>\n<pre><code class=\"language-java\">import UIKitclass CustomSegmentedControl: UISegmentedControl {        let selectedBackgroundColor = UIColor(red: 19\/255, green: 59\/255, blue: 85\/255, alpha: 0.5)    var sortedViews: [UIView]!    var currentIndex: Int = 0        required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        configure()    }        private func configure() {        sortedViews = self.subviews.sorted(by:{$0.frame.origin.x &lt; $1.frame.origin.x})        changeSelectedIndex(to: currentIndex)        self.tintColor = UIColor.white        self.layer.cornerRadius = 4        self.clipsToBounds = true        let unselectedAttributes = [NSForegroundColorAttributeName: UIColor.white,                                    NSFontAttributeName:  UIFont.systemFont(ofSize: 13, weight: UIFontWeightRegular)]        self.setTitleTextAttributes(unselectedAttributes, for: .normal)        self.setTitleTextAttributes(unselectedAttributes, for: .selected)    }        func changeSelectedIndex(to newIndex: Int) {        sortedViews[currentIndex].backgroundColor = UIColor.clear        currentIndex = newIndex        self.selectedSegmentIndex = UISegmentedControlNoSegment        sortedViews[currentIndex].backgroundColor = selectedBackgroundColor    }}<\/code><\/pre>\n<p>This process doesn\u2019t involve a lot of code, but it certainly took me some time to find a solution that gave me what I wanted. \u00a0Hopefully this saves you some trouble and gives you a new way of thinking about UI customization using Swiftin iOS.<\/p>\n<p>You can find the source code at\u00a0<a href=\"https:\/\/github.com\/JoeCoy\/CustomSegmentedControl\">https:\/\/github.com\/JoeCoy\/CustomSegmentedControl\u00a0<\/a><\/p>\n<p><!--kg-card-end: markdown--><\/p>\n<div style=\"background-color: #eceeff; padding: 30px; width: 80%;\">\n<h2>Build awesome things for fun.<\/h2>\n<p>Check out our current openings for your chance to make awesome things with creative, curious people.<\/p>\n<p><a href=\"https:\/\/hexboxdev.wpenginepowered.com\/careers-at-sep\/open-positions\/\">Explore SEP Careers \u00bb<\/a><\/p>\n<\/div>\n<p><strong>You Might Also Like<\/strong><\/p>\n<ul>\n<li><a href=\"https:\/\/hexboxdev.wpenginepowered.com\/blog\/swift-animation-tutorial-spritekit-xcode-swift\/\" target=\"_blank\" rel=\"noopener\">Swift Animation Tutorial \u2013 SpriteKit, Xcode, and Swift<\/a><\/li>\n<li><a href=\"https:\/\/hexboxdev.wpenginepowered.com\/blog\/alamofire-and-swiftyjson\/\" target=\"_blank\" rel=\"noopener\">Helpful Mobile Development Libraries: Alamofire and SwiftyJSON<\/a><\/li>\n<li><a href=\"https:\/\/hexboxdev.wpenginepowered.com\/blog\/checking-into-swift\/\" target=\"_blank\" rel=\"noopener\">Checking into Swift<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Customizing a UISegmentedControl If you have spent any time developing an iOS app, you have probably realized that customizing UI elements in Swift can be hard. There is either a checkbox that does what you want, or it takes hours of research and trial and error to get something just the way you want it. [&hellip;]<\/p>\n","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"content-type":"","associated_team_member":0,"associated_user_id":0,"footnotes":""},"categories":[273,268],"tags":[295,274,389,520],"service":[],"class_list":["post-11483","post","type-post","status-publish","format-standard","hentry","category-mobile","category-programming","tag-ios","tag-mobile","tag-swift","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/posts\/11483","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/comments?post=11483"}],"version-history":[{"count":0,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/posts\/11483\/revisions"}],"wp:attachment":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/media?parent=11483"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/categories?post=11483"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/tags?post=11483"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/service?post=11483"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}