简体   繁体   English

如何在 SwiftUI 中使用复选框创建文本?

[英]How can I create a text with Checkbox in SwiftUI?

I have a requirement of Checkbox (✅ as in to-do list) with textfield.我需要带有文本字段的复选框(✅ 如待办事项列表中所示)。 Currently I have created button object like below:目前我已经创建了按钮 object,如下所示:

    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")
        }
    }

I need to preserve checked and unchecked state in SwiftUI.我需要在 SwiftUI 中保留checkedunchecked的 state。

复选框样本图像

Here is a simple, re-usable checkmark component I created that follows a color scheme similar to other checkmarks on iOS (eg selecting messages in the Messages app):这是我创建的一个简单的、可重复使用的复选标记组件,其配色方案类似于 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()
    }
}

You can use it in other views like this:您可以在其他视图中使用它,如下所示:

...
@State private var checked = true
...

HStack {
    CheckBoxView(checked: $checked)
    Spacer()
    Text("Element that requires checkmark!")
}
...

The best way for iOS devices is to create a CheckboxStyle struct and conform to the ToggleStyle protocol. iOS 设备的最佳方法是创建 CheckboxStyle 结构并符合 ToggleStyle 协议。 That allows you to then use the built-in Toggle component provided by Apple.这使您可以使用 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())

On macOS, Apple already has created a CheckboxToggleStyle() that you can use for macOS 10.15+.在 macOS 上,Apple 已经创建了一个CheckboxToggleStyle() ,您可以将其用于 macOS 10.15+。 But it isn't available for iOS - yet.但它不适用于 iOS - 目前。

We can take help of the @State from Apple , which persists value of a given type, through which a view reads and monitors the value.我们可以从 Apple获得@State的帮助,它持久化给定类型的值,视图通过它读取和监视该值。

Working example:工作示例:

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)
  
    }
    
}

Now, you can add CheckboxFieldView()现在,您可以添加CheckboxFieldView()

You'll want something like this:你会想要这样的东西:

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()
    }
}

Your list could look something like this:您的列表可能如下所示:

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)
            })
        }
    }
}

Your model might look something like this:您的 model 可能看起来像这样:

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
    }
}

And finally a Todo class:最后是Todo class:

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

None of this has actually been tested and not all of the code has been implemented but this should get you on the right track.这些都没有经过实际测试,也没有所有的代码都已经实现,但这应该会让你走上正确的轨道。

Toggle seems to work for both macOS and iOS, using the native control on each. Toggle似乎适用于 macOS 和 iOS,对每个都使用本机控件。

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

A control that toggles between on and off states.在开启和关闭状态之间切换的控件。

@State var isOn: Bool = true

var body: some View {
   Toggle("My Checkbox Title", isOn: $isOn)
       .padding()
}

macOS:苹果系统:

macOS 复选框

iOS: iOS:

iOS 复选框

Here's my take on it.这是我的看法。 I'm actually doing this for MacOS, but the process should be the same.我实际上是为 MacOS 做的,但过程应该是一样的。

First, I had to fake the checkbox by creating two png images:首先,我必须通过创建两个 png 图像来伪造复选框: 在此处输入图像描述 and在此处输入图像描述 , calling them checkbox-on.png and checkbox-off.png respectively. ,分别称它们为checkbox-on.pngcheckbox-off.png These I put into Assets.xcassets .这些我放入Assets.xcassets

I believe that for iOS, the images are already available.我相信对于 iOS,图像已经可用。

Second, the view includes a state variable:其次,视图包含一个 state 变量:

@State var checked = false

The rest is to implement a Button with an action, an image, some text, and some modifiers: rest 是实现一个带有动作、图像、一些文本和一些修饰符的 Button:

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 is the boolean variable you want to toggle checked的是您要切换的 boolean 变量
  • The image depends on the value of the boolean, using the condition operator to choose between the two图像取决于boolean的值,使用条件运算符在两者之间进行选择
  • renderingMode() ensures that the image appears correctly and resizable() is used to enable frame() . renderingMode()确保图像正确显示,并且resizable()用于启用frame()
  • The rest of the modifiers are there to tweak the appearance.修改器的 rest 用于调整外观。

Obviously, if you are going to make a habit of this, you can create a struct:显然,如果你要养成这样的习惯,你可以创建一个结构:

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)
    }
}

and then use:然后使用:

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

(Note that you need to use self.$checked on this one). (请注意,您需要在此使用self.$checked )。

Finally, if you prefer to use a common alternative appearance, that of a filled in square for the check box, you can replace Image with:最后,如果您更喜欢使用常见的替代外观,即复选框的填充方块,您可以将Image替换为:

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

I learned a lot doing this, and hopefully, this will help.我在这方面学到了很多东西,希望这会有所帮助。

I found this solution here to be much better than using a completely custom made View :我发现这里的解决方案比使用完全定制的View要好得多:

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

He uses the ToggleStyle protocol to simply change the look of the toggle, instead of rebuilding it:他使用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() }
        }
    }
}

Here is my way:这是我的方式:

import SwiftUI

extension ToggleStyle where Self == CheckBoxToggleStyle {

    static var checkbox: CheckBoxToggleStyle {
        return CheckBoxToggleStyle()
    }
}

// Custom Toggle Style
struct CheckBoxToggleStyle: ToggleStyle {

    func makeBody(configuration: Configuration) -> some View {
        Button {
            configuration.isOn.toggle()
        } label: {
            Label {
                configuration.label
            } icon: {
                Image(systemName: configuration.isOn ? "checkmark.square.fill" : "square")
                    .foregroundColor(configuration.isOn ? .accentColor : .secondary)
                    .accessibility(label: Text(configuration.isOn ? "Checked" : "Unchecked"))
                    .imageScale(.large)
            }
        }
        .buttonStyle(PlainButtonStyle())
    }
}

struct ContentView: View {

    @State var isOn = false

    var body: some View {
    
        Toggle("Checkmark", isOn: $isOn).toggleStyle(.checkbox)

    }
}

Unchecked:未选中:

未选中:

Checked:检查:

检查:

You can use the following code and change the color etc. This is an individual component and I used a callback method to get informed when the checkbox is selected or not.您可以使用以下代码并更改颜色等。这是一个单独的组件,我使用回调方法来获取复选框是否被选中的通知。

Step 1: Create a customizable and reusable checkbox view Step 2: Let use the component in the main view Use the checkboxSelected() callback function to know which checkbox is selected or not.第 1 步:创建一个可自定义且可重用的复选框视图 第 2 步:让在主视图中使用该组件 使用 checkboxSelected() 回调 function 来知道哪个复选框被选中或未选中。

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()
    }
}

Selectable Circle, Customizable可选圆圈,可定制

struct SelectableCircle: View {
@Binding var isSelected: Bool
var selectionColor: Color = Color.green
var size: CGFloat = 20

var body: some View {
    ZStack {
        RoundedRectangle(cornerRadius: 10, style: .circular)
            .stroke(Color.gray, lineWidth: 2)
            .background(isSelected ? selectionColor : Color.clear)
            .frame(width: size, height: size, alignment: .center)
            .clipShape(Circle())
            .onTapGesture {
                withAnimation {
                    isSelected.toggle()
                }
            }
     }
  }
}

You can use like this:你可以这样使用:

struct CircleChooseView_Previews: PreviewProvider {
struct CircleChooseView: View {
    @State var checked = false

    var body: some View {
        HStack {
            SelectableCircle(isSelected: $checked)
            Text("Item that needs to be selected")
        }
        
    }
}

 static var previews: some View {
    CircleChooseView()
  }
}

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM