【问题标题】:How can I change Color appearance of Segmented Picker in SwiftUI?如何在 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 视频,我认为它非常适合更新视图更改,但我不知道如何翻译它。请问有人有什么建议或帮助吗?谢谢

【问题讨论】:

    标签: swift swiftui uikit


    【解决方案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 组件”。

      此外,与适用于所有UISegmentedControls 的应用程序范围内的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 而不是真正的方式的最佳方式,我也投反对票。
      猜你喜欢
      • 2020-01-04
      • 1970-01-01
      • 2013-02-20
      • 2013-02-14
      • 1970-01-01
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      相关资源
      最近更新 更多