73

I want to set the selected segment color in a SwiftUI segmented picker and change the text color to white.

I have tried both using the modifiers for the picker view and modifying the tint color from the appearance proxy. None of them seem to work, unfortunately.


import SwiftUI

struct PickerView: View {

    @State var pickerSelection = 0

    init() {
        UISegmentedControl.appearance().tintColor = UIColor.blue
    }

    var body: some View {
        Picker(selection: $pickerSelection, label: Text("")) {
            Text("Active").tag(0).foregroundColor(Color.white)
            Text("Completed").tag(1)
        }.pickerStyle(SegmentedPickerStyle()).foregroundColor(Color.orange)
    }
}

Is there any way to do this in SwiftUI, or should I just use the UISegmentedControl by using UIViewControllerRepresentable?

1
  • 2
    What should we do if we want to have different tint color for another segmented picker in the same view? Commented Jun 10, 2020 at 21:37

7 Answers 7

146

Native ( but limited )

SwiftUI is not currently supporting native SegmentedPicker styling (see the bottom of the answer for the working workaround). But there is a limited way to apply a color to the segmented picker using .colorMultiply() modifier:

enter image description here


Full control using UIAppearance

selectedSegmentTintColor is available since beta 3 for changing the color of the selected segment.

For changing the textColor, you should use setTitleTextAttributes for .selected state and .normal state (unselected).

So it will be like:

init() {
    UISegmentedControl.appearance().selectedSegmentTintColor = .blue
    UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
    UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.blue], for: .normal)
}

Segmented Controll

Also as mike mentioned, you can set the background color too like:

UISegmentedControl.appearance().backgroundColor = .yellow

Background demo

Also, don't forget you can set SwiftUI colors too! For example:

UISegmentedControl.appearance().selectedSegmentTintColor = UIColor(Color.accentColor)
Sign up to request clarification or add additional context in comments.

8 Comments

Note that there is no code completion for these properties, but if you type them they will compile
You can change the color of the unselected segments with UISegmentedControl.appearance().backgroundColor = .blue
Hey @mojtaba-hosseini @mike any idea how to have it be the AccentColor?
just set it like UIColor(Color.accentColor) @StuFFmc
It is better to limit the use of the appearance. If the parent-view is specific enough, then it is not necessary to reset the appearance with nil. That can affect the other components in the app. let appearance = UISegmentedControl.appearance(whenContainedInInstancesOf: [MyController.self])
|
9

Recently I had a similar issue that I needed a custom background and foreground colors for SwiftUI SegmentedControl I've found this post that also was helpful, but I also wanted to share other solution that I've found and I'm using now. There is a very nice package called SwiftUI Introspect that allows to, well...

Introspect underlying UIKit components from SwiftUI

With this package I created the following SegmentedControl enter image description here With this code:

VStack {
    Picker("Activity view", selection: $selectedActivity) {
        ForEach(ActivityView.allCases) { activity in
            Text(activity.description)
        }
    }
    .introspectSegmentedControl { segmentedControl in
        segmentedControl.backgroundColor = .clear
        segmentedControl.tintColor = TCHColors.SegmentedControl.backgroundSelected
        segmentedControl.selectedSegmentTintColor = TCHColors.SegmentedControl.backgroundSelected
        segmentedControl.setTitleTextAttributes([
            NSAttributedString.Key.foregroundColor: TCHColors.SegmentedControl.selectedText
        ], for: .selected)
        segmentedControl.setTitleTextAttributes([
            NSAttributedString.Key.foregroundColor: TCHColors.SegmentedControl.normalText
        ], for: .normal)
    }
    .pickerStyle(.segmented)
}
.padding(.horizontal, 16)

And you can use this package to access many other underlying components from SwiftUI.

2 Comments

I get an error "Cannot find 'TCHColors' in scope" can you update the code if you can tnx or just add comment to solve it please @Sangsom
Hey there, these are colors used from global app specific enum file, you just change to colors you want using UIColor instance, eg. UIColor.green.
2

Here is a manual implementation of segmented picker functionality:

@ViewBuilder func viewSegmentedButtons(arr: [String], selIndex: Int, baseColor: Color, closure:@escaping (_ selectedIndex: Int) -> Void) -> some View {
    
    let columns = Array(repeating: GridItem(spacing: 1), count:arr.count)
    LazyVGrid(columns: columns, spacing: 1.0) {
        ForEach(Array(arr.enumerated()), id: \.element) { index, translation in
            Button {
                closure(index)
            } label: {
                ZStack {
                    Rectangle()
                        .foregroundColor(index == selIndex ? baseColor : Color(.systemBackground))
                        .cornerRadius(radius: index==0 ? cRadius : 0, corners: [.topLeft, .bottomLeft])
                        .cornerRadius(radius: index==arr.count-1 ? cRadius : 0, corners: [.topRight, .bottomRight])
                    Text(translation)
                        .padding(.vertical, 10)
                        .font(.footnote)
                        .foregroundColor(index != selIndex ? baseColor : Color(.systemBackground) )
                }
            }
        }
    }
    .foregroundColor(baseColor)
    .overlay(
        RoundedRectangle(cornerRadius: cRadius)
            .stroke(baseColor, lineWidth: 2)
    )
    .font(.callout)
    .background(baseColor)
    .cornerRadius(cRadius)
    .padding(.bottom, 10)
}

Usage example:

@State private var levelIndex: Int = 0
var body: some View {
    VStack {
        Text("Level:")
        
        viewSegmentedButtons(arr: ["Easy", "Meduim", "Hard"], selIndex: levelIndex, baseColor: .orange) { selectedIndex in
            withAnimation {
                levelIndex = selectedIndex
            }
        }
    }
}

Result: enter image description here

1 Comment

available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) ''' Picker("Level:", selection: $level) { Text("All").tag(Level?.none) Text("Advance").tag(Level.advance as Level?) Text("Difficult").tag(Level.difficult as Level?) Text("Intermediate").tag(Level.intermediate as Level?) Text("Easy").tag(Level.easy as Level?) } .pickerStyle(.segmented) .colorMultiply(.callToAction) '''
1

Here is my solution that works in SwiftUI:

init(){

    UISegmentedControl.appearance().selectedSegmentTintColor = .green
    UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.black], for: .selected)

    }

Comments

1

This answered helped me, but there is something I would like to add. The color I used was an argument to the view, so placing these methods in an init did not allow me to access the color.

Alternatively, you can use an onAppear method directly on the Segmented Picker, like so.

import SwiftUI

struct PickerView: View {
    var color: UIColor   
    @State var pickerSelection = 0

    var body: some View {
        Picker(selection: $pickerSelection, label: Text("")) {
            Text("Active").tag(0).foregroundColor(Color.white)
            Text("Completed").tag(1)
        }.pickerStyle(SegmentedPickerStyle()).foregroundColor(Color.orange)
        .onAppear {
            UISegmentedControl.appearance().tintColor = color
        }
    }
}

Comments

1

As pointed out above by @Sangsom, you can use the SwiftUI Introspect package to achieve this.

Since the updated syntax on how to do this (using v1.3.0 of Introspect) is not in any of the current answers, I'll provide it below:

  1. Use SPM to add the Introspect package to your project file.

https://github.com/siteline/swiftui-introspect

Introspect SPM

  1. Import it at the top of your file
import SwiftUI
import SwiftUIIntrospect // Here
  1. Create your custom segmented picker and adjust it how you see fit.
@State private var selectedOption: Int = 0 // Tab Options

// Segmented Picker
Picker(selection: $selectedOption, label: Text("")) {
    Text("Option 1").tag(0)
    Text("Option 2").tag(1)
}
.introspect(.picker(style: .segmented), on: .iOS(.v16, .v17, .v18)) { segmentedControl in
    segmentedControl.backgroundColor = .clear
    segmentedControl.selectedSegmentTintColor = UIColor(.black)
    segmentedControl.setTitleTextAttributes([
        .foregroundColor: UIColor.white,
        .font: UIFont.systemFont(ofSize: 17)
    ], for: .selected)
    segmentedControl.setTitleTextAttributes([
        .foregroundColor: UIColor.black,
        .font: UIFont.systemFont(ofSize: 17)
    ], for: .normal)
}
.pickerStyle(SegmentedPickerStyle())
.cornerRadius(8)
.padding(.horizontal, 50)
.frame(height: 35)
.onChange(of: selectedOption) { newValue in
    switch newValue {
    case 0:
        print("Switched to Option 1 Tab")
    case 1:
        print("Switched to Option 2 Tab")
    default:
        break
    }
}

Result:

Picker Example

Comments

1
available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)

Picker("Level:", selection: $level) {
   Text("All").tag(Level?.none)
   Text("Advance").tag(Level.advance as Level?)
   Text("Difficult").tag(Level.difficult as Level?)
   Text("Intermediate").tag(Level.intermediate as Level?)
   Text("Easy").tag(Level.easy as Level?)
}
.pickerStyle(.segmented)
.colorMultiply(.accentColor)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.