Toggle
TextField
TextField 即为输入框,对应 UIKit 中的 UITextField。
struct ContentView: View {
var body: some View {
//第一个参数为placeholder,text为输入的内容,textFieldStyle为边框样式
TextField("写点什么进来吧", text: .constant("Hello"))
.textFieldStyle(RoundedBorderTextFieldStyle()) //圆角边框
.padding()
和 UIKit不同,SwiftUI是一个数据驱动的框架,故输入框输入的内容类型不再是 String,而是 Binding<String>,方便对输入内容的绑定操作。上面的例子暂时用了 constant(value: String)直接将一个字符串转成了Binding<String>。
SecureField
在 UIKit 中,我们只需要设置 UITextField 的 secureTextEntry 属性为 true 即可实现一个密码框,而在 SwiftUI 中,密码框变成了一个独立的 View 为 SecureField,使用方式与 TextField 基本相同。
struct ContentView: View {
var body: some View {
//第一个参数为placeholder
SecureField("请输入密码", text: .constant(""))
.textFieldStyle(RoundedBorderTextFieldStyle()) //圆角边框
.padding()
Button
Button 即按钮,对应 UIKit 中的 UIButton。
struct ContentView: View {
var body: some View {
Button("点击") {
//点击事件
print("成功点击")
这里使用了 Swift的尾随闭包来绑定点击按钮的事件。
如果我们要自定义这个按钮的具体外观,我们就需要实现 Button 构造函数中的 label 闭包。
struct ContentView: View {
var body: some View {
Button(action: {
print("666")
}) { //label闭包区域,设置按钮的样式
Image(systemName: "clock")
.renderingMode(.original) //绘制原图
.resizable()
.frame(width: 60, height: 60)
label闭包中不单单可以放一个控件,而是可以放多个控件和 Stack 容器进行布局。
实战:简易登录界面的构建
一个登录页面一般有四个元素:头像图片、输入用户名密码的两个输入框、登录按钮。在这里我们让他们纵向排列。设置当账号为 12345、密码为 6 位数字时登录成功,此时改变背景色为红色。
struct ContentView: View {
//使用@State修饰两个字符串,使得能用$符号访问后变成Binding<String>绑定在下面的text中
@State var username: String = ""
@State var password: String = ""
var body: some View {
VStack {
Image("profilephoto")
.resizable()
.frame(width: 80, height: 80)
.clipShape(Circle())
TextField("请输入用户名", text: $username, onEditingChanged: { isEditing in
// 正在编辑时触发
print("正在编辑\(isEditing)")
}, onCommit: {
//编辑完成后触发
print(self.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
SecureField("请输入密码", text: $password, onCommit: {
//编辑完成后触发
print(self.password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button("登录"){
//如果密码正确,改变背景色为红色
if self.username == "12345" && self.password.count == 6 {
//直接修改rootViewController
UIApplication.shared.windows.first?.rootViewController
= UIHostingController(rootView: Color.red)
在这个例子中我们需要注意,我们要通过属性包装类型 @State 来使得 username 和 password 字符串变成 Binding<String>,用于和两个输入框的输入内容绑定,从而实时获取输入框中的值。
Toggle
Toggle 为开关控件,对应 UIKit 中的 UISwitch。与 UIKit 不同,Toggle 需要绑定一个 Bool 类型的变量来控制开关的状态。
struct ContentView: View {
@State var isOpen = true
var body: some View {
Toggle(isOn: $isOpen) {
self.isOpen ? Text("开") : Text("关") //设置开关的标识文字随开关的状态而变化
.padding()
若我们不需要在开关状态变化时改变标识文字,我们可以简写。
Toggle("开关", isOn: $isShowing).padding()
若我们要使得开关能够一直保持打开或者关闭的状态,我们可以绑定一个常量,但这样开关就不能改变状态了。
Toggle("开关", isOn: .constant(true)).padding()
若我们需要更改开关的颜色,可以使用 toggleStyle 修饰符。
struct ContentView: View {
@State var isOpen = true
var body: some View {
Toggle("开关", isOn: $isOpen)
.toggleStyle(SwitchToggleStyle(tint: .red))
Slider
Slider 即滑块控件,对应 UIKit 中的 UISlider。我们要将其绑定到一个 Double 类型的变量来获取当前滑块的值。
struct ContentView: View {
@State var value: Double = 0.5 //绑定滑块
var body: some View {
VStack {
//in为滑块值的范围,step为步长
Slider(value: $value, in: 0...1, step: 0.1) { (bool) in
print(self.value)
Text("当前滑块的值为\(self.value)")
}.padding()
我们可以通过 VStack 和 HStack 容器的组合,实现一个亮度调节的界面。
struct ContentView: View {
@State var value: Double = 0.5 //绑定滑块
var body: some View {
VStack {
HStack{
Image(systemName: "sun.min")
Slider(value: $value, in: 0...1, step: 0.1) { (bool) in
print(self.value)
Image(systemName: "sun.max.fill")
}.padding()
Text("当前亮度为\(self.value)")
Stepper
Stepper 即步进控制器,对应 UIKit 中的 UIStepper。Stepper 的构造函数也和 Slider 十分类似。
struct ContentView: View {
@State var stepValue: Int = 0
var body: some View {
Stepper(value: $stepValue, in: 0...10, step: 1, label: {
Text("步进控制器的值为: \(self.stepValue)")
}) //这里的label同样可以写成尾随闭包的形式,这里将其完整的形式写了出来
若是纯文本的步进控制器,我们同样可以简写。
Stepper("步进控制器: \(self.stepValue)", value: $stepValue, in: 0...10)
如果我们想自定义点按步进控制器增加/减少按钮时执行的动作,可以实现 Stepper 构造函数中 onIncrement 和 onDecrement 两个闭包。
struct ContentView: View {
@State var stepValue: Int = 0
var body: some View {
Stepper(onIncrement: {
self.stepValue += 5
}, onDecrement: {
self.stepValue -= 3
}, label: { Text("步进控制器的值为: \(self.stepValue)") })
ForEach
ForEach 用于遍历一个数组或一个区间,从而创建多个 View。它为每个遍历到的数组或区间运行闭包,把当前数据项作为参数传入闭包。
struct ContentView: View {
var body: some View {
ForEach(0 ..< 20) { number in
Text("\(number)")
如果遍历的是区间,则只能是左闭右开区间( ..< )。
如果遍历的是数组,元素必须遵守 Identifiable 协议,假设不符合应该使用 id: \.self 作为第二个参数,因为使用 \.self 会生成传进 ForEach 中的那个数组对象(而不是传统意义下的 ContentView 实例化的对象)的散列值,并据此来唯一地标识数组内的每个元素。
struct ContentView: View {
let stringArray = ["abc", "bcd", "cde", "def"]
var body: some View {
ForEach(stringArray, id: \.self) { item in
Text("\(item)")
如果遍历的是数组,我们还可以使用如下的形式进行实现。
struct ContentView: View {
@State var names = ["ZhangSan", "LiSi", "WangWu"]
var body: some View {
// offset表示下标
ForEach(Array(names.enumerated()), id: \.offset) { turple in
HStack {
Text("\(turple.offset)")
Text(turple.element)
如果我们需要遍历的是一个自定义类型的数组,此时有两种处理方式:
1、使得自定义类型遵守 Identifiable 协议,并且加入一个 id 属性用于唯一的表示某个对象。
2、使用“id: \.某个用于唯一判断的属性名”作为第二个参数传入 ForEach。
//第一种实现方式
struct User: Identifiable {
var id = UUID() //id定义为Int型也是可以的,但是下面构造的时候必须要指定对象的唯一编号
var name: String
struct ContentView: View {
let users = [User(name: "zhangsan"), User(name: "lisi"), User(name: "wangwu")]
var body: some View {
//这里不用传第二个参数id
ForEach(users) { user in
Text(user.name)
//第二种实现方式
struct User {
var name: String
struct ContentView: View {
let users = [User(name: "zhangsan"), User(name: "lisi"), User(name: "wangwu")]
var body: some View {
//这里必须要传第二个参数id
ForEach(users, id: \.name) { user in
Text(user.name)
Picker
Picker 即选择器,对应 UIKit 中的 UIPickerView。默认情况下会有一个文字提示当前选择器的功能,当然我们可以选择关闭。在使用 Picker 时,我们需要绑定一个 Int 型变量来记录当前选择的索引值。
struct ContentView: View {
@State var index: Int = 0 //picker的索引
let dataSource = ["红", "橙", "黄", "绿", "青", "蓝", "紫"]
var body: some View {
VStack{
Picker(selection: $index, label: Text("选择颜色")) {
ForEach(0..<dataSource.count) {
Text(self.dataSource[$0])
Text(dataSource[index]) //显示选中的结果
UIKit 中的 UISegmentControl,在 SwiftUI 中是一个特殊的 Picker,只需设置 pickerStyle 修饰符即可。
struct ContentView: View {
var items = ["红","黄","紫"]
@State var currentIndex: Int = 0
var body: some View {
VStack{
Picker("颜色", selection: $currentIndex) {
ForEach(0 ..< self.items.count) { index in
Text(self.items[index])
.tag(index)
.pickerStyle(SegmentedPickerStyle()) //设置picker的样式,以实现先前的UISegmentControl
.padding()
Text(items[currentIndex])
labelsHidden()
用于隐藏 Picker、Stepper、Toggle 等标签文本,如果希望隐藏所有标签,可以将此修饰符应用在最外层容器。
struct ContentView: View {
@State var index: Int = 0
let dataSource = ["红", "橙", "黄", "绿", "青", "蓝", "紫"]
var body: some View {
VStack{
Picker(selection: $index, label: Text("选择颜色")) {
ForEach(0..<dataSource.count) {
Text(self.dataSource[$0])
.labelsHidden() //隐藏文本
Text(dataSource[index])
将View存储为属性
可以将 View 及其 Modifier 提取出来作为属性使用。
struct ContentView: View {
let title = Text("20200721")
.font(.largeTitle)
let subtitle = Text("星期二")
.foregroundColor(.secondary)
var body: some View {
VStack {
//调用前面定义的属性
title
.foregroundColor(.red)
subtitle
发布于 2020年7月21日 2023年2月16日 作者 louyu 分类 iOS与Swift
ICP备案号:苏ICP备19001595号-3
公安备案号:苏公网安备32050602011398号
隐私政策 友情链接