如何在 SwiftUI 中更改分段选取器的颜色外观?
Posted
技术标签:
【中文标题】如何在 SwiftUI 中更改分段选取器的颜色外观?【英文标题】:How can I change Color appearance of Segmented Picker in SwiftUI? 【发布时间】:2021-04-14 17:50:02 【问题描述】:有谁知道如何做到这一点?我有红色、绿色、黄色和蓝色四种颜色,我希望我选择的片段具有指示颜色名称的背景/色调颜色。 这是我的代码:
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)
这是我要从中提取的列表
enum Color: String, CaseIterable
case red, yellow, green, blue
我尝试过使用 onChange 或 onReceive(以及 Combine 的 'Just()' 用于子视图)而不是 onAppear,但它们在 Playground 上崩溃并且在 Xcode 上不起作用。 我还看到了一个关于 UIAction 的 WWDC 视频,我认为它非常适合更新视图更改,但我不知道如何翻译它。请问有人有什么建议或帮助吗?谢谢
【问题讨论】:
【参考方案1】:这里:既然您使用的是enum
,那么选择的类型也应该是enum
类型,而且您对自定义enum
的命名也有误。
版本 1.0.0:
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
【讨论】:
【参考方案2】:我刚刚用它的新版本更新了我的旧答案,现在你可以在你的视图中使用 AdvancedSegmentedPicker,并像普通的 SegmentedPicker 一样使用它您可以更改和更新 SegmentedPicker 上的所有颜色,例如:
背景颜色
selectedSegmentTintColor
selectedSegmentForegroundColor
normalSegmentForegroundColor
你可以这样使用:
AdvancedSegmentedPicker(items:selectedItem:)
或者:
AdvancedSegmentedPicker(items:selectedItem:backgroundColor:selectedSegmentTintColor:selectedSegmentForegroundColor:normalSegmentForegroundColor:)
还请注意,只要您将 items
和 selectedItem
提供给 AdvancedSegmentedPicker,您可以在任何项目中使用它,也可以进行颜色自定义。
2.0.0 版:
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
【讨论】:
此解决方案不适用于同一页面中的两个** Picker**【参考方案3】:这是您需要的简单版本。这个答案使用SwiftUI-Introspect 来“从 SwiftUI 内省底层 UIKit 组件”。
此外,与适用于所有UISegmentedControl
s 的应用程序范围内的UISegmentedControl.appearance()
不同,这只影响可能更有用的特定应用程序。
这是工作代码:
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
结果:
【讨论】:
我不认为这是使用 pod 而不是真正的方式的最佳方式,我也投反对票。以上是关于如何在 SwiftUI 中更改分段选取器的颜色外观?的主要内容,如果未能解决你的问题,请参考以下文章