基础UI
- TextField的numbers
- 多种方式创建Button
- SecureField
- Slider
- Picker
- DatePicker
- Segment
- Stepper
- TextEditor
- ColorPicker
- ProgressView
- ProgressView显示菊花
- Map
- ButtonRepeat
- Map的大头针
- Link
- MultiDatePicker
- LabelsHidden
概述
文章主要分享SwiftUI Modifier的学习过程,将使用案例的方式进行说明。内容浅显易懂,对结果大部分进行了截图展示,也有偷懒的部分,不过测试代码是齐全的。如果想要运行结果,可以移步Github下载code -> github案例链接
1、TextField的numbers
1.1、.number
@State private var score = 0
// format格式化
VStack(alignment: .leading) {
TextField("Enter your score", value: $score, format: .number)
.textFieldStyle(.roundedBorder)
Text("Your score was \(score)")
}
.padding()
1.2、NumberFormatter
@State private var score = 0
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
//formatter格式化
VStack(alignment: .leading) {
TextField("Enter your score", value: $score, formatter: formatter)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("Your score was \(score)")
}
.padding()
2、SecureField
SwiftUI的SecureField
的原理与TextField几乎相同,有保护隐私而屏蔽字符的功能。
@State private var password: String = ""
VStack(alignment: .leading) {
SecureField("Enter a password", text: $password)
Text("Your entered: \(password)")
}
.padding()
3、toggle
SwiftUI的toggle
可以在true和false状态之间切换,就像UISwitch在UIKit中一样。
3.1、toggle基础式样
//定义一个@state属性包装的bool类型的值,用于存储切换的当前值,然后根据需要使用它来显示或隐藏操作。
@State private var showGreeting = true
Toggle("Show welcome message", isOn: $showGreeting)
if showGreeting {
Text("Hi,BBLv")
}
3.2、使用.toggleStyle自定义按钮颜色
@State private var showGreeting = true
Toggle("Show welcome message", isOn: $showGreeting)
.toggleStyle(SwitchToggleStyle(tint: .red))
3.3、使用.toggleStyle(.button)自定义整个文字颜色
@State private var showGreeting = true
Toggle("Show welcome message", isOn: $isOn)
.toggleStyle(.button)
.tint(.mint)
3.4、批量订阅
从iOS16开始,可以将Toggle绑定到bool数组,可以批量禁用或者开启。例如,订阅
struct EmailList: Identifiable {
var id: String
var isSubscribed = false
}
@State var lists = [
EmailList(id: "Monthly Updates", isSubscribed: true),
EmailList(id: "News Flashes", isSubscribed: true),
EmailList(id: "Spacial offers", isSubscribed: true)
]
Form {
Section {
ForEach($lists) { $list in
Toggle($list.id, isOn: $list.isSubscribed)
}
}
Section {
Toggle("Subscribe to all", sources: $lists, isOn: \.isSubscribed)
}
}
调试结果:
4、创建Slider并读取值
SwiftUI的Slider
的原理与UISlider相似,你需要将其绑定到某处一边可以存储值。创建一个Slider需要的常规参数:
- value:绑定一个Double类型的数据
- in:slider的滑动范围
- Step:移动时值的变化量。
VStack(alignment: .leading) {
Slider(value: $celsius, in: -100...100)
Text("\(celsius, specifier: "%.1f") Celsius is \(celsius * 9 / 5 + 32, specifier: "%.1f") fahrenheit")
}
.padding()
调试结果
5、创建Picker并读取值
SwiftUI的Picker将UIPickerView
、UISegmentedControl
合二为一,同时还适应其他OS上的样式。与大多数其他空间一样,你必须将picker附加到@State来跟踪picker的选择。例如,创建一个colors数组和一个整数来存储选择的颜色,然后将其与选择器和文本一起使用。
var colors = ["Red", "Green", "Blue", "Tartan"]
@State private var selectedColor = "Red"
VStack {
Picker("Plaease choose a color", selection: $selectedColor) {
ForEach(colors, id: \.self) {
Text($0)
}
}
Text("Your selected: \(selectedColor)")
}
调试结果:
6、创建DatePicker并读取值
SwiftUI的DatePicker
类似于UIDatePicker,并带有多种选项来控制其样式与工作方式,与所有存储值的空间一样,它需要绑定到应用程序中的某种状态。创建一个绑定到birthDate的datePicker,通过选择日期,然后显示设置的datePicker值
6.1、基本样式
@State private var birthDate = Date.now
@State private var date = Date.now
VStack {
DatePicker(selection: $birthDate, in: ...Date.now, displayedComponents: .date) {
Text("Select a date")
}
Text("Date is \(birthDate.formatted(date: .long, time: .omitted))")
}
.padding()
6.2、GraphicalDatePickerStyle
从iOS14开始,使用GraphicalDatePickerStyle
获得更高级的日期选择
VStack {
Text("Enter your birthday")
.font(.largeTitle)
DatePicker("Enter your birthday", selection: $date)
.datePickerStyle(GraphicalDatePickerStyle())
.frame(maxHeight: 400)
}
.padding()
调试结果:
7、创建Segment并读取值
SwiftUI的Picker还可以用于创建UISegmentedControl一样的分段控件,尽管它需要帮到到某种状态,并且确保为每一个segment
提供一个标记,以便可以识别,segment可以时文字或图片。
7.1、基础样式
创建一个与favoriteColor状态属性一起使用的segment,并在下面添加一个Test,显示所选的值
@State private var favoriteColor = 0
Picker("What is your favrite color?", selection: $favoriteColor) {
Text("Red").tag(0)
Text("Green").tag(1)
Text("Blue").tag(2)
}
.pickerStyle(.segmented)
Text("Value: \(favoriteColor)")
7.2、通过遍历创建
@State private var favoriteColorStr = "Red"
var colors = ["Red", "Green", "Blue"]
Picker("What is your favrite color?", selection: $favoriteColorStr) {
ForEach(colors, id: \.self) {
Text($0)
}
}
.pickerStyle(.segmented)
Text("Value: \(favoriteColorStr)")
调试结果:
8、创建Stepper并读取值
SwiftUI的Stepper
与UIStepper相同
8.1、基础样式
创建一个setpper绑定到age,取值范围0-130
@State private var age = 18
Stepper("Enter your age", value: $age, in: 0...130)
Text("Your age is \(age)")
8.2、onIncrement自定义闭包
@State private var age = 18
Stepper("Enter your age") {
age += 1
} onDecrement: {
age -= 1
}
Text("Your age is \(age)")
调试结果:
9、TextEditor创建多行可编辑文本
SwiftUI的TextEditor
用于处理多行滚动文本的视图。可以根据需要设置字体、更改颜色,甚至可以调整行距和行数
@State private var profileText = "Enter your bio"
NavigationStack {
TextEditor(text: $profileText)
.foregroundStyle(.secondary)
.padding(.horizontal)
.navigationTitle("About you")
}
调试结果
10、ColorPicker选择颜色
10.1、基础样式
@State private var bgColor = Color.red
@State private var bgColor1 = Color.green
VStack {
ColorPicker("Set the background color", selection: $bgColor)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(bgColor)
.padding()
10.2、禁用透明度选项
默认情况下,ColorPicker支持颜色选择中的不透明度,可以禁用
@State private var bgColor = Color.red
@State private var bgColor1 = Color.green
VStack {
ColorPicker("Set the background color", selection: $bgColor1, supportsOpacity: false)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(bgColor1)
.padding()
调试结果:
11、ProgressView显示任务进度
11.1、基础样式
创建一个正在下载的进度条
@State private var downloadAmount = 0.0
VStack {
ProgressView("Downloading...", value: downloadAmount, total: 100)
}
11.2、动态填满进度条
@State private var downloadAmount = 0.0
let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
ProgressView("Downloading...", value: downloadAmount, total: 100)
.onReceive(timer, perform: { _ in
if downloadAmount < 100 {
downloadAmount += 2
}
})
调试结果:
12、ProgressView显示不确定的进度
ProgressView可以在不附加任何类型的绑定的情况下创建,这种情况下显示一个菊花,不带进度
ProgressView("Downloading...")
调试结果:
13、Map
通过MKCoordinateRegion创建经纬度
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
Map(coordinateRegion: $region)
.frame(width: 400, height: 300)
调试结果:
14、buttonRepeat
SwiftUI有一个专用的buttonRepeatBehavior()
修饰符,跟随点击,每次点击都会被响应
14.1、基础样式
@State private var tapCount = 0
Button("Tap Count: \(tapCount)") {
tapCount += 1
}
.buttonRepeatBehavior(.enabled)
14.2、通过shift+return来反复触发我们的按钮
@State private var tapCount = 0
Button("Tap Count: \(tapCount)") {
tapCount += 1
}
.buttonRepeatBehavior(.enabled)
.keyboardShortcut(.return, modifiers: .shift)
调试结果:
15、Map的大头针
swiftUI的Map可以在地图上标注注释,包括默认的标记和引脚,以及完全自定义的视图。
- 创建某种状态,以跟踪地图显示的坐标,使用
MKCoordinateRegion
跟踪地图的中心和缩放级别。 - 准备一系列位置用于你的注释
- 决定你希望它如何显示在你的地图上。你可以显示内置的
MapPin
和MapMaker
注释,也可以使用自己的视图。
无论你创建的什么位置数组,你的类型都必须符合Identifiable
,以便SwiftUI了解如何唯一的识别每个项目。
//如果你拥有的只是CLLocationCoordinate2D数据,你可以通过向这样的扩展来直接使用他们。
extension CLLocationCoordinate2D: Identifiable {
public var id: String {
"\(latitude)-\(longitude)"
}
}
//它把所有的东西放在一起,这样你就可以看到各个首都城市的大头针
struct City: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))
let annotations = [
City(name: "London", coordinate: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275)),
City(name: "Paris", coordinate: CLLocationCoordinate2D(latitude: 48.8567, longitude: 2.3508)),
City(name: "Rome", coordinate: CLLocationCoordinate2D(latitude: 41.9, longitude: 12.5)),
City(name: "Washington DC", coordinate: CLLocationCoordinate2D(latitude: 38.89511, longitude: -77.036667))
]
Map(coordinateRegion: $region, annotationItems: annotations) {
MapPin(coordinate: $0.coordinate)
}
.frame(width: 400, height: 300)
调试结果
16、Link
Link("BBlv的掘金主页", destination: URL(string: "https://juejin.cn/user/1196684132486568")!)
//由于它是一个文本链接,可以使用字体、颜色等修饰
Link("BBlv的掘金主页", destination: URL(string: "https://juejin.cn/user/1196684132486568")!)
.font(.title)
.foregroundStyle(.red)
//如果通过自定义视图,而不是文本
Link(destination: URL(string: "https://juejin.cn/user/1196684132486568")!, label: {
Image(systemName: "link.circle.fill")
.font(.largeTitle)
})
//在上下文环境中获取OpenUrl
Button("Visit Apple") {
openURL(URL(string: "https://juejin.cn/user/1196684132486568")!)
}
调试结果:
17、MultiDatePicker选择多个日期
SwiftUI的MultiDatePicker
显示日历视图,用户能同时选择各种日期,无论是从任何可能的日期还是从你选择的日期范围
@Environment(\.calendar) var calendar
@State var dates: Set<DateComponents> = []
List {
Section {
MultiDatePicker("Select your preferred dates", selection: $dates)
}
Section {
//在环境中读取日历,并转换为数据
MultiDatePicker("Select your preferred dates", selection: $dates)
Text(summary)
}
Section {
//添加选取时间限制,在今天开始往后的时间
MultiDatePicker("Select your preferred dates", selection: $dates, in: Date.now...)
Text(summary)
}
}
var summary: String {
dates.compactMap { components in
calendar.date(from: components)?.formatted(date:.long, time:.omitted)
}.formatted()
}
调试结果:
18、LabelsHidden()隐藏Picker、Stepper、Toggle等标签
SwiftUI要求我们在其控件中添加标签,通常想要隐藏这些标签,以便你可以获得更精确的UI布局。
@State private var selectedNumber = 0
Picker("Select a number", selection: $selectedNumber) {
ForEach(0..<10) {
Text("\($0)")
}
}
.labelsHidden()