Toggle 开关

Toggle 是一个水平方向扩张型的视图,垂直方向则不扩张。

Toggle 是一个可以打开或关闭的开关。像其他控件一样,你需要把它绑定到一个变量上,这个变量被传递到 Toggle 的初始化器中。然后需要做的是改变这个绑定变量的值来改变 Toggle 的状态,使其开启或关闭。或者读取绑定变量的值来查看 Toggle 当前处于什么状态。

// 简单文字开关
Toggle("Night Mode", isOn: $isToggleOn)

// SwiftUI 中同一段代码在不同设备上可以产生不同的 UI 。例如以上代码对于 iOS 和 iPadOS,Toggle 会渲染成开关;而在 macOS 会渲染成单选框。SwiftUI 的美妙之处是你可以针对所有 Apple 平台复用大部分的代码,无需修改。

参数:isOn 绑定布尔值

// 宣告一個状态变量來储存目前开关的设定
@State private var showCheckInOnly = false

Toggle(isOn: $showCheckInOnly) { ... }

参数:视图闭包

// 一种写法:是把 Toggle 的 Label 当作闭包视图,这样可以在里面写比较复杂的视图作为 Toggle 的左侧视图
Toggle(isOn: $showCheckInOnly) {
    Text("开关的Label")
}

Toggle(isOn: $showCheckInOnly) {
		VStack {
				Text("Enable Dark Mode")
        Text("开关的Label")
        Image(systemName: "moon")
    }
}

// 另一种简单的写法,不需要视图闭包,直接设置 Label 文字
Toggle("Show welcome message", isOn: $showGreeting)

// 在这种写法里,如果把字符串设置为空,会发现开关左侧仍然预留了一点的空间。如果想彻底隐藏,需要用到修饰符
Toggle("", isOn: $isToggleOn)        
	.labelsHidden()

Toggle 样式设置

toggleStyle 修饰符

SwiftUI 中内置的 toggleStyle 切换样式包括:

// 使用 toggleStyle 修饰符还可以自定义开关的颜色
Toggle("Show welcome message", isOn: $showGreeting)
		.toggleStyle(SwitchToggleStyle(tint: .red))

// 如果您的目标平台是 iOS 15 或更高版本,则可以通过指定 .toggleStyle(.button) 将开关配置为显示为按钮
// 在此模式下,按钮在其状态打开时翻转其色调颜色:
Toggle("Filter", isOn: $isOn)
		.toggleStyle(.button)
		.tint(.mint)

<aside> 💡

toggleStyle 不能用 accentColor 修改颜色,应该用 .tint() 修饰符更改颜色。

</aside>

ToggleStyle 协议实现自定义

SwiftUI 提供了 ToggleStyle 协议来自定义 Toggle 开关的外观和工作方式。任何符合此协议的结构都必须实现 makeBody() 方法,该方法可以按照您的需要呈现 Toggle ,并且您将同时提供用于切换的标签和 isOn 绑定,您可以翻转它来调整切换。

重要提示:当您自定义 Toggle 开关时,您需要自己以某种方式切换打开状态,SwiftUI 不会为您做这件事。

以下演示自定义 Toggle 样式,这里使用按钮来翻转打开状态,然后添加自定义标签来显示该状态。我没有使用像标准 iOS Toggle 这样的移动圆圈,而是将此显示为两个 SF 符号之一:

struct CheckToggleStyle: ToggleStyle {
		func makeBody(configuration: Configuration) ->some View {
        Button {
            configuration.isOn.toggle()
        } label: {
            Label {
                configuration.label
            } icon: {
                Image(systemName: configuration.isOn ? "checkmark.circle.fill" : "circle")
                    .foregroundStyle(configuration.isOn ? Color.accentColor : .secondary)
                    .accessibility(label: Text(configuration.isOn ? "Checked" : "Unchecked"))
                    .imageScale(.large)
            }
        }
        .buttonStyle(.plain)
    }
}

// An example view showing the style in action
struct ContentView: View {
    @State private var isOn = false
		var body:some View {
        Toggle("Switch Me", isOn: $isOn)
            .toggleStyle(CheckToggleStyle())
    }
}

Toggle 应用

实现一对多绑定效果

该功能在 Xcode 中无法预览。

从 iOS 16 开始,还可以将 Toggle 绑定到布尔数组,这在您想要同时启用或禁用多个值时很有帮助。例如,我们可以编写一些代码来让用户订阅单个新闻通讯,或者通过一个开关来切换所有新闻通讯:

struct EmailList: Identifiable {
    var id: String
    var isSubscribed = false
}

struct ContentView: View {

    @State var lists = [
        EmailList(id: "Monthly Updates", isSubscribed: true),
        EmailList(id: "News Flashes", isSubscribed: true),
        EmailList(id: "Special Offers", isSubscribed: true)
    ]

    var body: some View {
        Form {
            Section {
                ForEach($lists) { $list in
                    Toggle(list.id, isOn: $list.isSubscribed)
                }
            }

						// 源是输入数组,绑定该数组的某个键路径(也就是一组布尔值)
            Section {
                Toggle("Subscribe to all", sources: $lists, isOn: \\.isSubscribed)
            }
        }
    }
}