Reputation: 1151
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?
Upvotes: 72
Views: 42663
Reputation: 2245
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:
Introspect
package to your project file.import SwiftUI
import SwiftUIIntrospect // Here
@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:
Upvotes: 0
Reputation: 660
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
}
}
}
}
Upvotes: 3
Reputation: 391
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
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.
Upvotes: 9
Reputation: 120022
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:
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)
}
Also as mike mentioned, you can set the background color too like:
UISegmentedControl.appearance().backgroundColor = .yellow
Also, don't forget you can set SwiftUI colors too! For example:
UISegmentedControl.appearance().selectedSegmentTintColor = UIColor(Color.accentColor)
Upvotes: 141
Reputation: 858
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
}
}
}
Upvotes: 1
Reputation: 147
Here is my solution that works in SwiftUI:
init(){
UISegmentedControl.appearance().selectedSegmentTintColor = .green
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.black], for: .selected)
}
Upvotes: 1