【问题标题】:How can I create a text with Checkbox in SwiftUI?如何在 SwiftUI 中使用 Checkbox 创建文本?
【发布时间】:2022-01-28 07:28:03
【问题描述】:

我需要带有文本字段的 复选框(✅ 与待办事项列表一样)。目前我已经创建了如下按钮对象:

    Button(action: {
            // do when checked / unchecked
            //...
    }) {
        HStack(alignment: .top, spacing: 10) {

            Rectangle()
                .fill(Color.white)
                .frame(width:20, height:20, alignment: .center)
                .cornerRadius(5)
            Text("Todo  item 1")
        }
    }

我需要在 SwiftUI 中保留 checkedunchecked 状态。

【问题讨论】:

    标签: ios swift checkbox swiftui


    【解决方案1】:

    这是我创建的一个简单、可重复使用的复选标记组件,其配色方案类似于 iOS 上的其他复选标记(例如,在消息应用中选择消息):

    import SwiftUI
    
    struct CheckBoxView: View {
        @Binding var checked: Bool
    
        var body: some View {
            Image(systemName: checked ? "checkmark.square.fill" : "square")
                .foregroundColor(checked ? Color(UIColor.systemBlue) : Color.secondary)
                .onTapGesture {
                    self.checked.toggle()
                }
        }
    }
    
    struct CheckBoxView_Previews: PreviewProvider {
        struct CheckBoxViewHolder: View {
            @State var checked = false
    
            var body: some View {
                CheckBoxView(checked: $checked)
            }
        }
    
        static var previews: some View {
            CheckBoxViewHolder()
        }
    }
    

    您可以像这样在其他视图中使用它:

    ...
    @State private var checked = true
    ...
    
    HStack {
        CheckBoxView(checked: $checked)
        Spacer()
        Text("Element that requires checkmark!")
    }
    ...
    

    【讨论】:

    • 太棒了!这么简单的 hack :)
    【解决方案2】:

    iOS 设备的最佳方式是创建一个 CheckboxStyle 结构并符合 ToggleStyle 协议。这样您就可以使用 Apple 提供的内置 Toggle 组件。

    // CheckboxStyle.swift
    import SwiftUI
    
    struct CheckboxStyle: ToggleStyle {
    
        func makeBody(configuration: Self.Configuration) -> some View {
    
            return HStack {
                Image(systemName: configuration.isOn ? "checkmark.square" : "square")
                    .resizable()
                    .frame(width: 24, height: 24)
                    .foregroundColor(configuration.isOn ? .blue : .gray)
                    .font(.system(size: 20, weight: .regular, design: .default))
                    configuration.label
            }
            .onTapGesture { configuration.isOn.toggle() }
    
        }
    }
    
    // Example usage in a SwiftUI view
    Toggle(isOn: $checked) {
        Text("The label")
    }
    .toggleStyle(CheckboxStyle())
    

    在 macOS 上,Apple 已经创建了一个 CheckboxToggleStyle(),您可以将其用于 macOS 10.15+。但它还不适用于 iOS。

    【讨论】:

      【解决方案3】:

      我们可以借助@Statefrom Apple,这是一个给定类型的持久值,视图通过它读取和监视该值。

      工作示例:

      struct CheckboxFieldView : View {
      
          @State var checkState:Bool = false ;
      
          var body: some View {
      
               Button(action:
                  {
                      //1. Save state
                      self.checkState = !self.checkState
                      print("State : \(self.checkState)")
      
      
              }) {
                  HStack(alignment: .top, spacing: 10) {
      
                              //2. Will update according to state
                         Rectangle()
                                  .fill(self.checkState ? Color.green : Color.red)
                                  .frame(width:20, height:20, alignment: .center)
                                  .cornerRadius(5)
      
                         Text("Todo  item ")
      
                  }
              }
              .foregroundColor(Color.white)
      
          }
      
      }
      

      现在,您可以添加CheckboxFieldView()

      【讨论】:

        【解决方案4】:

        你会想要这样的:

        struct TodoCell: View {
            var todoCellViewModel: TodoCellViewModel
            var updateTodo: ((_ id: Int) -> Void)
        
            var body: some View {
                HStack {
                    Image(systemName: (self.todoCellViewModel.isCompleted() ? "checkmark.square" : "square")).tapAction {
                        self.updateTodo(self.todoCellViewModel.getId())
                    }
        
                    Text(self.todoCellViewModel.getTitle())
                }
                .padding()
            }
        }
        

        您的列表可能如下所示:

        struct TodoList: View {
            var todos: Todos
            var updateTodo: ((_ id: Int) -> Void)
        
            var body: some View {
                List(self.todos) { todo in
                    TodoCell(todoCellViewModel: TodoCellViewModel(todo: todo), updateTodo: { (id) in
                        self.updateTodo(id)
                    })
                }
            }
        }
        

        您的模型可能如下所示:

        public class TodoCellViewModel {
        
            private var todo: Todo
        
            public init(todo: Todo) {
                self.todo = todo
            }
        
            public func isCompleted() -> Bool {
                return self.todo.completed
            }
        
            public func getTitle() -> String {
                return self.todo.title
            }
        
            public func getId() -> Int {
                return self.todo.id
            }
        }
        

        最后是Todo 类:

        public class Todo: Codable, Identifiable {    
            public let id: Int
            public var title: String
            public var completed: Bool
        }
        

        这些都没有经过实际测试,也不是所有的代码都已经实现,但这应该会让你走上正确的轨道。

        【讨论】:

          【解决方案5】:

          这是我的看法。我实际上是为 MacOS 做的,但过程应该是一样的。

          首先,我必须通过创建两个 png 图像来伪造复选框:,分别称它们为 checkbox-on.pngcheckbox-off.png。这些我放入Assets.xcassets

          我相信对于 iOS,图像已经可用。

          二、视图包含一个状态变量:

          @State var checked = false
          

          剩下的就是实现一个带有动作、图像、一些文本和一些修饰符的按钮:

          Button(action: {
              checked.toggle()
          }) {
              Image(checked ? "checkbox-on" :  "checkbox-off")
                  .renderingMode(.original)
                  .resizable()
                  .padding(0)
                  .frame(width: 14.0, height: 14.0)
                  .background(Color(NSColor.controlBackgroundColor))
              Text("Choose me … !").padding(0)
          }
          .buttonStyle(PlainButtonStyle())
          .background(Color(red: 0, green: 0, blue: 0, opacity: 0.02))
          .cornerRadius(0)
          
          • checked 是您要切换的布尔变量
          • 图像取决于布尔值,使用条件运算符在两者之间进行选择
          • renderingMode() 确保图像正确显示,resizable() 用于启用frame()
          • 其余的修饰符用于调整外观。

          显然,如果你要养成这样的习惯,你可以创建一个结构体:

          struct Checkbox: View {
              @Binding var toggle: Bool
              var text: String
              var body: some View {
                  Button(action: {
                      self.toggle.toggle()
                  }) {
                      Image(self.toggle ? "checkbox-on" :  "checkbox-off")
                          .renderingMode(.original)
                          .resizable()
                          .padding(0)
                          .frame(width: 14.0, height: 14.0)
                          .background(Color(NSColor.controlBackgroundColor))
                      Text(text).padding(0)
                  }
                  .buttonStyle(PlainButtonStyle())
                  .background(Color(red: 0, green: 0, blue: 0, opacity: 0.02))
                  .cornerRadius(0)
              }
          }
          

          然后使用:

          Checkbox(toggle: self.$checked, text: "Choose me … !")
          
          

          (注意这个需要使用self.$checked)。

          最后,如果您更喜欢使用常见的替代外观,即复选框的填充方块,您可以将Image 替换为:

          Rectangle()
              .fill(self.autoSave ? Color(NSColor.controlAccentColor) : Color(NSColor.controlColor))
              .padding(4)
              .border(Color(NSColor.controlAccentColor), width: 2)
              .frame(width: 14, height: 14)
          

          我在这方面学到了很多东西,希望这会有所帮助。

          【讨论】:

          • 非常感谢。它节省了我的时间。
          【解决方案6】:

          Toggle 似乎对 macOS 和 iOS 都有效,在两者上都使用本机控件。

          https://developer.apple.com/documentation/swiftui/toggle

          在开启和关闭状态之间切换的控件。

          @State var isOn: Bool = true
          
          var body: some View {
             Toggle("My Checkbox Title", isOn: $isOn)
                 .padding()
          }
          

          macOS:

          iOS:

          【讨论】:

            【解决方案7】:

            我发现这里的解决方案比使用完全定制的View 要好得多:

            https://swiftwithmajid.com/2020/03/04/customizing-toggle-in-swiftui/

            他使用ToggleStyle 协议来简单地改变开关的外观,而不是重建它:

            struct CheckboxToggleStyle: ToggleStyle {
                func makeBody(configuration: Configuration) -> some View {
                    return HStack {
                        configuration.label
                        Spacer()
                        Image(systemName: configuration.isOn ? "checkmark.square" : "square")
                            .resizable()
                            .frame(width: 22, height: 22)
                            .onTapGesture { configuration.isOn.toggle() }
                    }
                }
            }
            

            【讨论】:

              【解决方案8】:

              您可以使用以下代码并更改颜色等。这是一个单独的组件,我使用了一个回调方法来通知复选框是否被选中。

              第 1 步:创建可自定义且可重复使用的复选框视图 第二步:让我们在主视图中使用组件 使用 checkboxSelected() 回调函数知道哪个复选框被选中。

              import SwiftUI
              
              //MARK:- Checkbox Field
              struct CheckboxField: View {
                  let id: String
                  let label: String
                  let size: CGFloat
                  let color: Color
                  let textSize: Int
                  let callback: (String, Bool)->()
              
                  init(
                      id: String,
                      label:String,
                      size: CGFloat = 10,
                      color: Color = Color.black,
                      textSize: Int = 14,
                      callback: @escaping (String, Bool)->()
                      ) {
                      self.id = id
                      self.label = label
                      self.size = size
                      self.color = color
                      self.textSize = textSize
                      self.callback = callback
                  }
              
                  @State var isMarked:Bool = false
              
                  var body: some View {
                      Button(action:{
                          self.isMarked.toggle()
                          self.callback(self.id, self.isMarked)
                      }) {
                          HStack(alignment: .center, spacing: 10) {
                              Image(systemName: self.isMarked ? "checkmark.square" : "square")
                                  .renderingMode(.original)
                                  .resizable()
                                  .aspectRatio(contentMode: .fit)
                                  .frame(width: self.size, height: self.size)
                              Text(label)
                                  .font(Font.system(size: size))
                              Spacer()
                          }.foregroundColor(self.color)
                      }
                      .foregroundColor(Color.white)
                  }
              }
              
              enum Gender: String {
                  case male
                  case female
              }
              
              struct ContentView: View {
                  var body: some View {
                      HStack{
                          Text("Gender")
                              .font(Font.headline)
                          VStack {
                              CheckboxField(
                                  id: Gender.male.rawValue,
                                  label: Gender.male.rawValue,
                                  size: 14,
                                  textSize: 14,
                                  callback: checkboxSelected
                              )
                              CheckboxField(
                                  id: Gender.female.rawValue,
                                  label: Gender.female.rawValue,
                                  size: 14,
                                  textSize: 14,
                                  callback: checkboxSelected
                              )
                          }
                      }
                      .padding()
                  }
              
                  func checkboxSelected(id: String, isMarked: Bool) {
                      print("\(id) is marked: \(isMarked)")
                  }
              }
              
              struct ContentView_Previews: PreviewProvider {
                  static var previews: some View {
                      ContentView()
                  }
              }
              

              【讨论】:

                【解决方案9】:
                猜你喜欢
                • 1970-01-01
                • 2019-10-28
                • 1970-01-01
                • 1970-01-01
                • 2020-12-24
                • 1970-01-01
                • 1970-01-01
                • 2021-02-12
                • 1970-01-01
                相关资源
                最近更新 更多