警告
此文档参考了Chatgpt的解答
@Binding
在SwiftUI中,@Binding
是一种属性包装器,用于将属性绑定到另一个属性的状态。它提供了一种双向数据绑定的方式,使得组件基于状态自动更新。@Binding用于独立的变量之间双向绑定,将子视图和父视图的状态同步。
例如,如果您想要创建一个可以在父View和子View之间传递值的视图,则可以使用@Binding。假设您在父View中定义了一个变量,该变量将传递给子View:
struct ParentView: View {
@State var value = 0
var body: some View {
ChildView(value: $value)
}
}
struct ParentView: View {
@State var value = 0
var body: some View {
ChildView(value: $value)
}
}
在子View中,您可以使用@Binding将该变量进行绑定:
struct ChildView: View {
@Binding var value: Int
var body: some View {
// 使用绑定的值
}
}
struct ChildView: View {
@Binding var value: Int
var body: some View {
// 使用绑定的值
}
}
这样,当父View的value值发生变化时,子View的value值也会自动更新。在子视图中更新该值会同步更新父视图中的值。
与@State的区别
虽然@Binding和@State都可以用于处理应用程序状态,但它们在用途和使用方式上有很大的不同。
@State被用于在单个视图中自动管理状态,它是一个属性包装器,用于在视图结构内部声明状态属性,当状态变化时自动触发视图更新。
而@Binding是用于连接不同的视图之间的双向数据绑定。它是一个传递数据的工具,其中数据可以在两个不同的视图之间传递。一般情况下,@Binding在父视图中传递一个值给子视图,并且允许子视图修改该值并将其更新回父视图。
另外,@State只能用于一个视图中,而@Binding可以用于将值在不同视图之间传递。此外,@State只能由一个特定视图修改,而@Binding可以通过多个视图修改。
因此,尽管它们都可以处理状态,但@Binding和@State的用途和使用方式是不同的。只有当您需要将两个或多个视图之间的值保持同步时才应使用@Binding。而对于每个视图的本地状态,应该使用@State。
@EnvironmentObject
@ EnvironmentObject是一种属性包装器,用于将环境对象注入到视图层次结构中的任何地方,并使其可用于所有层次结构中的子视图。环境对象是一个可观察的对象,可以包含任意类型的数据,并且可以在整个应用程序中共享和使用,例如应用程序的设置或用户身份验证。@EnvironmentObject属性包装器的使用涉及以下步骤:
- 创建一个ObservableObject的自定义类并放入一些需要跨视图共享的属性。
- 在应用程序的顶层视图中使用@EnvironmentObject注册此自定义类。
- 在需要访问环境对象的任何视图中使用@EnvironmentObject属性包装器获取访问这个环境对象的能力。
总之,@EnvironmentObject在 SwiftUI 的应用中为我们提供了一种方便的方式,以将环境对象注入到整个应用程序的视图层次结构中。
示例代码:
import SwiftUI
class UserData: ObservableObject {
@Published var name = "John"
}
struct ContentView: View {
@EnvironmentObject var userData: UserData
var body: some View {
VStack {
Text("Welcome, \(userData.name)!")
.font(.title)
.foregroundColor(.green)
TextField("Enter your name", text: $userData.name)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(UserData())
}
}
import SwiftUI
class UserData: ObservableObject {
@Published var name = "John"
}
struct ContentView: View {
@EnvironmentObject var userData: UserData
var body: some View {
VStack {
Text("Welcome, \(userData.name)!")
.font(.title)
.foregroundColor(.green)
TextField("Enter your name", text: $userData.name)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(UserData())
}
}
在这个例子中,我们创建了一个名为UserData的ObservableObject。该对象具有一个@Published属性name,它将在数据更改时自动发布更改通知。然后,我们定义了一个名为ContentView的视图。该视图使用@EnvironmentObject注释将userData对象注入其上下文中,以便在视图本身和其子视图中可用。接下来,我们在视图中放置了一些简单的UI元素,例如标题和文本字段。文本字段使用$userData.name来读取和更新UserData的name属性。最后,我们还为ContentView定义了一个名为ContentView_Previews的视图预览,该预览使用.environmentObject将UserData注入ContentView以显示在预览中。
通过使用@EnvironmentObject将可观察对象注入SwiftUI视图树,我们可以使数据更改在整个应用程序中自动同步,并使数据管理变得简单。
@StateObject
@StateObject 是一个属性包装器,用于将对象作为 State 在视图中进行管理。SwiftUI中,每当属性的值更改时,所有依赖该值的视图都会重新计算和更新,@StateObject 通过将其包装的对象作为State来管理其状态,并确保只创建一个实例。当对象的状态改变时,SwiftUI会自动重新计算视图。
使用 @StateObject 的好处是可以将对象的生命周期委托给 SwiftUI 管理,也可以方便地在不同的视图之间共享相同的实例。这对于需要使用长时间存在的对象(例如网络请求、数据库连接或其他长时间运行的任务)的视图非常有用。
@StateObject只有iOS14以后版本才能用。iOS13之前,可以使用@ObservedObject
。
StateObject 和 ObservedObject 的区别:
@StateObject vs @ObservedObject: The differences explained - SwiftLee
Observed objects marked with the @StateObject property wrapper don’t get destroyed and re-instantiated at times their containing view struct redraws.
如果view有局部变量是用ObservedObject
修饰的,该view被重绘的时候,这个局部变量也会被重置。 但是如果我们用@StateObject
修饰,就不会有这个问题。
也就是说,如果我们使用变量ObservedObject
是从外部视图传参过来的,这样@StateObject
没有区别
@State
@Published
SwiftUI 中的 @Published 属性包装器是用于标记特定属性的特殊属性包装器。被 @Published 标记的属性实际上是一个 ObservableObject
引用,并且当该属性的值被修改时,系统会自动发布一个对象更改的通知。订阅了该属性的视图会自动更新,并显示该属性的最新值。
举个例子:
class DataModel: ObservableObject {
@Published var itemName = ""
@Published var itemCount = 0
// ...
}
class DataModel: ObservableObject {
@Published var itemName = ""
@Published var itemCount = 0
// ...
}
在上述示例中, itemName 和 itemCount 属性被标记为 @Published。这意味着每当它们的值发生变化时,他们会自动将更改推送到任何依赖于他们的视图。
@Published 是 SwiftUI 框架中一项非常强大的功能,它能够大幅减少代码量,并能够自动管理数据驱动的视图更新。
aspectRatio
SwiftUI中的aspectRatio可以帮助我们调整视图的宽高比。它通常用于调整图片或视频的大小并保持固定宽高比。
aspectRatio有两种用法:
- aspectRatio(_ aspectRatio: CGFloat, contentMode: ContentMode):此函数接受两个参数,第一个参数是宽高比,第二个参数是内容模式。例如,aspectRatio(16/9, contentMode: .fit)指定了视图的宽高比为16:9,且图像会缩放以适应视图。
- aspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode):此函数接受CGSize类型的参数,表示宽高比。例如,aspectRatio(CGSize(width: 3, height: 4), contentMode: .fill)指定视图高度是宽度的4/3,且图像将填充整个视图。
可以在视图的modifier中使用aspectRatio()来设置视图的宽高比。例如:
Image("example_image")
.resizable()
.aspectRatio(16/9, contentMode: .fit)
Image("example_image")
.resizable()
.aspectRatio(16/9, contentMode: .fit)
这将显示一个16:9宽高比的可调整大小的图像,并将其缩放以适应视图。
LengthFormatter
Swift中的LengthFormatter是一个用于格式化长度(例如距离)的工具类。它可以将表示长度的数字转换为字符串,并根据所需的格式进行格式化。
LengthFormatter支持多种格式选项,包括以不同单位(如英尺、米、千米)表示长度,以及在文本中添加度量单位符号等。
例如,以下代码将一个Double值表示为“1.23 km”字符串:
let lengthFormatter = LengthFormatter()
let distance = 1234.56
let formattedDistance = lengthFormatter.string(fromValue: distance, unit: .kilometer)
print(formattedDistance) // "1.23 km"
let lengthFormatter = LengthFormatter()
let distance = 1234.56
let formattedDistance = lengthFormatter.string(fromValue: distance, unit: .kilometer)
print(formattedDistance) // "1.23 km"
LengthFormatter还可以根据地区设置显示格式。例如,在德国,LengthFormatter会将公里表示为“km”,而在美国,它会将其表示为“mi”。
此外,LengthFormatter还支持在数字前面或后面添加文本,例如“距离:1.23 km”。
KeyPath
Swift5.2引入了 KeyPath<Root,Value>,这是个泛型类型。 参考: 探索Swift中KeyPath的使用 - 掘金
// 用来表示从 Root 类型到某个 Value 属性的访问路径.既然它是一个类型,你就可以在变量中存储、传递、操作这个类型。
// 例如: 这里代表 Hike.Observation 这个类型如何访问到 Range<Double>
var path: KeyPath<Hike.Observation, Range<Double>>
// 用来表示从 Root 类型到某个 Value 属性的访问路径.既然它是一个类型,你就可以在变量中存储、传递、操作这个类型。
// 例如: 这里代表 Hike.Observation 这个类型如何访问到 Range<Double>
var path: KeyPath<Hike.Observation, Range<Double>>
根据keyPath访问:
model[keyPath: mypath]
model[keyPath: mypath]
数组操作中的 map和lazy.map
在Swift中,数组操作的map和lazy.map都是用于对数组中的元素进行转换操作的方法,但它们的执行方式有所不同。
- map方法会直接生成一个新的数组,并将转换后的元素存放在其中,需要等待所有的转换操作都执行完后,才会返回一个新的数组。这种方式产生了临时的数组实例,当原数组比较大时,可能会占用较多的内存空间。
- lazy.map方法则是使用惰性计算的方式,先创建一个新的序列对象,然后在遍历这个序列的同时,对每个元素进行转换操作,并返回一个新的序列对象。只有在需要这些元素时,才会对转换操作进行实际的计算,避免了一次性分配大量的内存空间。
因此,当需要对一个大数组进行转换操作时,如果使用map方法,会产生大量的临时数组,占用大量的内存;而使用lazy.map方法,可以避免这种情况,减少内存的占用。但是,由于使用了惰性计算,所以在对结果进行遍历时,可能会出现一定的性能影响。
日期格式化
Text("Goal Date: ") + Text(profile.goalDate, style: .date)
Text("Goal Date: ") + Text(profile.goalDate, style: .date)
cornerRadius(.infinity)
在 SwiftUI 中,cornerRadius() 是一个用于设置视图的圆角的修饰符。而 cornerRadius(.infinity) 表示将视图的圆角设置为无限大,使得视图的四个角完全变为圆形,没有任何角度。
这种设置在创建按钮、卡片、标签等圆形或圆角视图时很有用。通常情况下,我们可以设置一个固定的半径来产生具有一定角度的圆角效果,但是当我们想要完全没有角度时,就需要将半径设置为无限大,即 cornerRadius(.infinity)
。这样的设置可以使一个矩形视图变为一个完全圆形的视图,或使视图的四个角完全变为圆形,没有任何锐角。
ios16 中的NavigationStack 和 NavigationPath
ios - NavigationView and NavigationLink on button click in SwiftUI? - Stack Overflow
In iOS 16 we can access the NavigationStack and NavigationPath.
ios - SwiftUI - how to avoid navigation hardcoded into the view? - Stack Overflow
@Environment
@Environment是SwiftUI的一种特殊属性,可以获取当前应用程序的环境变量,并让环境变量的值可用于视图中。
@Environment可以获取当前应用程序的环境变量,例如:
- .colorScheme:获取当前使用的外观色彩方案(明/暗)。
- .presentationMode:获取当前包含视图的PresentationMode(例如,是否以全屏模式呈现该视图)。
- .locale:获取当前语言环境。
通过获取@Environment对象,您可以轻松地在您的视图中使用这些环境变量的值,并根据需要调整视图的外观或行为。
另外,在SwiftUI中,可以使用@EnvironmentObject,通过设置属性观察器来自动监控环境对象的变化。这使得您可以轻松地自动更新您的视图,而无需编写繁琐的手动代码。
resizable
在 SwiftUI 中,resizable 是用于调整视图大小的修饰符。它可以与任何可调整大小的视图元素(如 Image 和 Shape)一起使用。
使用 resizable 时,可以将其与可选参数一起使用,即指定所需大小的宽度和高度。接下来,SwiftUI 将按比例缩放视图元素,以适应指定的大小。
例如,可以在 SwiftUI 中使用如下代码调整图像的大小:
Image("example-image")
.resizable()
.frame(width: 200, height: 200)
Image("example-image")
.resizable()
.frame(width: 200, height: 200)
以上代码指定了该图像元素的宽度和高度均为 200 点。在这个例子中,使用 resizable() 修饰符告诉 SwiftUI 图像可以缩放大小,以使其适应到确定的大小。
另外,如果不指定宽度和高度参数,那么 SwiftUI 将自动调整视图元素的大小,以使其适应所在的容器大小。