Reputation: 81
Does anyone know how to achieve this? I have four colors of red, green, yellow, and blue and I want my selected segment to have a background/tint color that is indicative of the color name. This is my code:
let objectColors = Color.allCases
@State private var selectedColorIndex = 0
//declared inside body view
Picker("Colors", selection: $selectedColorIndex) {
ForEach(0..<objectColors.count){ index in
Text(self.objectColors[index].rawValue).tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
.padding(10)
.onAppear {
UISegmentedControl.appearance().selectedSegmentTintColor = UIColor.generateUIColor(colorIndex: selectedColorIndex)
}
This is the list I'm pulling from
enum Color: String, CaseIterable {
case red, yellow, green, blue
}
I've tried using onChange or onReceive (and Combine's 'Just()' for subview) instead of onAppear but they crash on playgrounds and don't work on Xcode. I also saw a WWDC video on UIAction that I think will work great for updating view changes but I have no idea how to translate it. Does anyone one have any suggestions or help, please? Thanks
Upvotes: 2
Views: 5726
Reputation: 30461
Here is the simple version of what you need. This answer uses SwiftUI-Introspect to "Introspect underlying UIKit components from SwiftUI".
In addition, instead of UISegmentedControl.appearance()
which is app-wide for all UISegmentedControl
s, this only affects this specific one which can be a lot more useful.
Here is the working code:
struct ContentView: View {
let objectColors = SegmentColor.allCases
@State private var selectedColor = SegmentColor.red
var body: some View {
Picker("Colors", selection: $selectedColor) {
ForEach(objectColors) { color in
Text(color.rawValue).tag(color)
}
}
.introspectSegmentedControl { segmentedControl in
segmentedControl.selectedSegmentTintColor = selectedColor.color
}
.pickerStyle(SegmentedPickerStyle())
.padding(10)
.onChange(of: selectedColor) { _ in }
}
}
enum SegmentColor: String, CaseIterable, Identifiable {
case red, yellow, green, blue
var id: String { rawValue }
var color: UIColor {
switch self {
case .red: return .red
case .yellow: return .yellow
case .green: return .green
case .blue: return .blue
}
}
}
Result:
Upvotes: -1
Reputation: 1
I just updated my old Answer with new Version of it, now you can just use AdvancedSegmentedPicker in your View, and work with it like a normal SegmentedPicker with this deference that you are able to change and update all colors on SegmentedPicker, like:
backgroundColor
selectedSegmentTintColor
selectedSegmentForegroundColor
normalSegmentForegroundColor
you could use like:
AdvancedSegmentedPicker(items:selectedItem:)
Or:
AdvancedSegmentedPicker(items:selectedItem:backgroundColor:selectedSegmentTintColor:selectedSegmentForegroundColor:normalSegmentForegroundColor:)
Also Notice that this AdvancedSegmentedPicker is not hard coded for just this project, as long as you gave items
and selectedItem
to AdvancedSegmentedPicker, you can use it in any project, and you could do your color customization as well.
import SwiftUI
struct ContentView: View {
var items: [ColorEnum] = ColorEnum.allCases
@State private var selectedItem: ColorEnum = ColorEnum.blue
@State private var backgroundColor: UIColor?
@State private var selectedSegmentTintColor: UIColor?
@State private var selectedSegmentForegroundColor: UIColor?
@State private var normalSegmentForegroundColor: UIColor?
var body: some View {
VStack(spacing: 30.0) {
AdvancedSegmentedPicker(items: items, selectedItem: $selectedItem, backgroundColor: backgroundColor, selectedSegmentTintColor: selectedSegmentTintColor,
selectedSegmentForegroundColor: selectedSegmentForegroundColor, normalSegmentForegroundColor: normalSegmentForegroundColor)
Text("You selected: " + selectedItem.rawValue)
.bold()
.shadow(radius: 10.0)
.padding()
Button("update backgroundColor") {
if backgroundColor == UIColor.gray { backgroundColor = nil }
else { backgroundColor = UIColor.gray }
}
Button("update selectedSegmentForegroundColor") {
if selectedSegmentForegroundColor == UIColor.white { selectedSegmentForegroundColor = nil }
else { selectedSegmentForegroundColor = UIColor.white }
}
Button("update normalSegmentForegroundColor") {
if normalSegmentForegroundColor == UIColor.white { normalSegmentForegroundColor = nil }
else { normalSegmentForegroundColor = UIColor.white }
}
}
.font(Font.body.bold())
.onAppear() { selectedSegmentTintColor = selectedItem.ColorValue }
.onChange(of: selectedItem) { newValue in selectedSegmentTintColor = newValue.ColorValue }
.padding()
.background(Color.black.opacity(0.1).cornerRadius(10.0))
.padding()
.statusBar(hidden: true)
}
}
struct AdvancedSegmentedPicker<T: CustomStringConvertible & Hashable>: View {
var items: [T]
@Binding var selectedItem: T
var backgroundColor: UIColor? = nil
var selectedSegmentTintColor: UIColor? = nil
var selectedSegmentForegroundColor: UIColor? = nil
var normalSegmentForegroundColor: UIColor? = nil
@State private var renderView: Bool = Bool()
var body: some View {
return Group {
if renderView { SegmentedPickerView(items: items, selectedItem: $selectedItem, backgroundColor: backgroundColor, selectedSegmentTintColor: selectedSegmentTintColor,
selectedSegmentForegroundColor: selectedSegmentForegroundColor, normalSegmentForegroundColor: normalSegmentForegroundColor) }
else { SegmentedPickerView(items: items, selectedItem: $selectedItem, backgroundColor: backgroundColor, selectedSegmentTintColor: selectedSegmentTintColor,
selectedSegmentForegroundColor: selectedSegmentForegroundColor, normalSegmentForegroundColor: normalSegmentForegroundColor) }
}
.onChange(of: backgroundColor) { _ in renderView.toggle() }
.onChange(of: selectedSegmentTintColor) { _ in renderView.toggle() }
.onChange(of: selectedSegmentForegroundColor) { _ in renderView.toggle() }
.onChange(of: normalSegmentForegroundColor) { _ in renderView.toggle() }
}
}
private struct SegmentedPickerView<T: CustomStringConvertible & Hashable>: View {
var items: [T]
@Binding var selectedItem: T
init(items: [T], selectedItem: Binding<T>, backgroundColor: UIColor? = nil,
selectedSegmentTintColor: UIColor? = nil, selectedSegmentForegroundColor: UIColor? = nil, normalSegmentForegroundColor: UIColor? = nil) {
self.items = items
self._selectedItem = selectedItem
UISegmentedControl.appearance().backgroundColor = backgroundColor
UISegmentedControl.appearance().selectedSegmentTintColor = selectedSegmentTintColor
if let unwrappedSelectedSegmentForegroundColor: UIColor = selectedSegmentForegroundColor {
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: unwrappedSelectedSegmentForegroundColor], for: .selected)
}
else {
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.label], for: .selected)
}
if let unwrappedNormalSegmentForegroundColor: UIColor = normalSegmentForegroundColor {
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: unwrappedNormalSegmentForegroundColor], for: .normal)
}
else {
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.label], for: .normal)
}
}
var body: some View {
Picker("", selection: $selectedItem) {
ForEach(items, id: \.self) { item in
Text(String(describing: item))
}
}
.pickerStyle(SegmentedPickerStyle())
}
}
enum ColorEnum: String, CaseIterable, CustomStringConvertible { case red, yellow, green, blue
var ColorValue: UIColor {
get {
switch self {
case .red: return UIColor.red
case .yellow: return UIColor.yellow
case .green: return UIColor.green
case .blue: return UIColor.blue
}
}
}
var description: String {
get { return self.rawValue }
}
}
Upvotes: 0
Reputation: 1
Here: Since you are using enum
then selected type should be enum
type as well, also you had wrong naming for your custom enum
.
struct ContentView: View {
@State private var selectedColor: ColorEnum = ColorEnum.blue
@State private var renderView: Bool = Bool()
var body: some View {
Group {
if renderView { SegmentedPickerView(selectedColor: $selectedColor) }
else { SegmentedPickerView(selectedColor: $selectedColor) }
}
.onChange(of: selectedColor) { _ in renderView.toggle() }
}
}
struct SegmentedPickerView: View {
@Binding var selectedColor: ColorEnum
init(selectedColor: Binding<ColorEnum>) {
self._selectedColor = selectedColor
UISegmentedControl.appearance().selectedSegmentTintColor = selectedColor.wrappedValue.ColorValue
}
var body: some View {
VStack {
Picker("", selection: $selectedColor) {
ForEach(ColorEnum.allCases, id: \.self) { item in
Text(item.rawValue)
}
}
.pickerStyle(SegmentedPickerStyle())
Text("You selected: " + selectedColor.rawValue)
.bold()
.shadow(radius: 10.0)
.padding()
}
.padding()
.background(Color.black.opacity(0.1).cornerRadius(10.0))
.foregroundColor(Color(selectedColor.ColorValue))
.padding()
}
}
enum ColorEnum: String, CaseIterable {
case red, yellow, green, blue
var ColorValue: UIColor {
get {
switch self {
case .red: return UIColor.red
case .yellow: return UIColor.yellow
case .green: return UIColor.green
case .blue: return UIColor.blue
}
}
}
}
Upvotes: 2