SwiftUI学习(3)
1.结构体和类在状态管理中的不同行为
1)@State 属性包装器:
- @State 属性包装器用于在 SwiftUI 视图中管理简单的本地状态。当该属性的值发生更改时,视图会自动重新渲染以反映最新的状态。
- @State 属性应该仅用于在单个视图内部共享数据,因为它是局部的。如果需要在多个视图之间共享数据,可以考虑使用 @StateObject、@ObservedObject 或 @EnvironmentObject 等属性包装器。
2)结构体 vs 类:
在 SwiftUI 中,使用结构体和类来定义视图和数据模型的行为有所不同。
- 结构体是值类型,类是引用类型。
- 当使用结构体时,每次更改结构体的属性时,实际上是创建了一个新的结构体实例。这种行为确保了每次属性更改时视图都会重新加载,因为 SwiftUI 可以检测到结构体实例的变化。
- 相比之下,类是引用类型,多个引用可以指向同一个类实例。如果使用类来管理状态,当类内部的属性更改时,视图不会自动重新加载,除非使用 @ObservedObject 或 @StateObject 等属性包装器来通知 SwiftUI 属性的更改。
- 例子
struct改变时上面显示也会更改,class则不会
3)mutating 关键字:
- 在结构体中,如果需要在方法内部修改结构体的属性,需要使用 mutating 关键字来标记该方法。这是因为结构体是值类型,方法默认情况下是不允许修改结构体内部属性的。
- 在类中,不需要使用 mutating 关键字,因为类是引用类型,可以直接修改类的属性而不需要额外标记方法。
如果需要在多个视图之间共享数据,可以考虑使用引用类型(类)并结合适当的属性包装器来实现。
4)@Published 和 @ObservedObject
在SwiftUI中,@Published 和 @ObservedObject 是一对属性包装器,用于实现视图与数据模型之间的双向绑定和自动更新。这种使用方式使得 SwiftUI 应用程序能够实现简单而强大的数据绑定,使视图与数据模型之间保持同步,同时减少了手动更新视图的工作量。
- @Published:用于标记属性,当属性的值发生更改时,自动向订阅该属性的视图发送通知,以便更新视图。
- @ObservedObject:用于在视图中观察对象的更改。当被标记为 @ObservedObject 的对象发生更改时,视图会自动重新加载以反映最新的对象状态。
@Published 用于标记 User 类中的 firstName 和 lastName 属性,以便在这两个属性的值发生更改时自动更新视图。而在 ContentView 结构体中,通过 @ObservedObject 属性包装器将 User 类的实例与视图进行绑定,以便在用户更改 firstName 和 lastName 时,自动更新视图中显示的内容
2.视图
1)sheet
在SwiftUI中,sheet 是一个用于在当前视图上显示模态视图的修饰符。通过 sheet 修饰符,您可以在用户与应用程序交互时弹出一个新的视图.
1.创建一个布尔值该表是否显示
2.当我们点击按钮时,将显示表注释替换为表
3.将工作表附加到我们视图结构某处类似alert
4.决定实际应该在工作表放些什么
从底部向上查看幻灯片,向下拖动将器其关闭
将"cc" 作为参数传递给 Secondview 的 name 属性。
注:关闭当前视图
点击一个Dismiss按钮通过 presentationMode.wrappedValue.dismiss() 来关闭当前视图的呈现。
使用 @Environment 属性包装器来声明一个名为 presentationMode 的属性,该属性将用于访问视图的呈现模式,以便在需要时可以控制视图的呈现和消失。
2)删除行
可以左滑点击删除,也可以iOS滑动删除
添加导航栏,完成编辑按钮,允许用户删除几行
struct ContentView: View {
@State private var numbers = [Int]()
@State private var currentNumber = 1
var body: some View {
NavigationView{
VStack{
List{//循环显示数组 numbers 中的元素
ForEach(numbers,id: \.self){
Text("\($0)")
}
.onDelete(perform: removeRows)
//表示用户可以通过滑动删除的方式删除列表中的行
}
Button("Add Number"){
self.numbers.append(self.currentNumber)
//将变量 currentNumber 的值添加到数组 numbers 的末尾
self.currentNumber += 1
}
}
.navigationBarItems(leading: EditButton())//导航栏编辑按钮
}
}//用于从数组 numbers 中移除指定索引位置的元素
func removeRows(at offsets: IndexSet){
numbers.remove(atOffsets: offsets)
}
}
3) 图像设置
VStack {
GeometryReader{ geo in
Image("图标")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:geo.size.width)
//.clipped()
}
}
1)GeometryReader 是 SwiftUI 中的一个视图容器,用于获取父视图的几何信息并将其传递给其子视图。动态调整图像视图的大小,使其填充父视图的宽度。
2).resizable()修饰符用于使图像视图可以调整大小。将其与.aspectRatio(contentMode: .fit)一起使用时,图像将按照适合其容器的方式进行调整,同时保持其原始宽高比
3)应用.clipped()修饰符时,告诉SwiftUI只显示视图边界框内的内容,超出边界框的内容将被裁剪掉。
4)滚动视图
ScrollView是一个用于显示滚动内容的视图容器。ScrollView允许用户在内容太大而无法适应屏幕时进行滚动查看。
.frame(maxWidth: .infinity)是用于设置视图最大宽度为无限大的修饰符。这意味着视图将会占据其父视图可用的所有水平空间,使其在水平方向上填充整个可用空间。即为
可以在整个视图中滑动
5)导航链接
hello world像一个按钮,点击会产生一个新的视图,从右侧详细查看幻灯片,导航标题成为后退按钮,可以点击或从左边缘返回
- NavigationLink用于在SwiftUI应用程序中创建导航链接,使用户能够导航到其他视图。
- 当用户点击具有NavigationLink修饰符的视图时,系统会自动推送目标视图到导航堆栈中,从而实现导航。
注:工作表与导航链接
都允许我们显示当前新视图
导航链接用于显示有关所选择用户的详细信息
如设置,或者撰写窗口
工作表是为了显示不相关的内容
6)绘制图形
在 SwiftUI 中,Path 用于创建自定义的形状或路径
path.move设置起始点
path.addLine增加一段路径
.stroke() 是用于给形状或路径添加描边的方法
可以看到设置描边后,上面缺角,所以需要增加一条路径
.stroke(Color.blue,style: StrokeStyle(lineWidth: 10,lineCap: .round,lineJoin: .round))设置圆角
可以创建一个名为 Triangle 的结构体,并让它遵循 Shape 协议,来绘制图形
clockwise顺时针
InsettableShape 是一个协议(protocol),用于表示可设置插图(inset)的形状。实现了 InsettableShape 协议的自定义形状可以定义一个额外的插图区域,该区域会在原始形状的边界内部进行缩放,
它返回了一个新的形状,该形状在原始形状的基础上进行了插图(inset)操作。
- stride(from:to:by:) 方法会从起始值开始,逐步增加一个固定的步长,直到达到但不包括结束值。
- CGAffineTransform(rotationAngle: CGFloat) 是一个用于创建旋转变换的方法
- concatenating 方法用于将两个 CGAffineTransform 进行连接(concatenate),即将它们相乘得到一个新的变换
- CGAffineTransform(translationX: rect.width / 2, y: rect.height / 2) 是一个平移变换,它将坐标系向右平移 rect.width / 2 个单位,向下平移 rect.height / 2 个单位。
- .applying(position) 方法将旋转变换应用到这个点上,得到了变换后的新点
3.用户默认值
iOS 提供了多种方式来进行数据的读写操作,其中包括用户默认值、文件系统、Core Data、以及数据库等。
用户默认值是 iOS 中一种简单的持久化存储方式,适合存储少量用户数据,如用户设置、应用程序状态等。
- 存储数据类型:用户默认值适合存储基本数据类型、数组、字典等简单数据结构。
- 适用范围:适用于小型数据量的存储,如用户设置、应用程序状态等。
- 自动加载:存储在用户默认值中的数据会在应用启动时自动加载,方便在应用程序中访问。
- 存储限制:用户默认值适合存储少量数据,如果存储大量数据,可能会导致应用启动速度变慢。
- 数据类型陷阱:在使用用户默认值存储数据时,需要注意数据类型的正确性。由于强类型语言如 Swift,每个常量和变量都具有特定类型,因此在存储和读取数据时要确保类型匹配,避免出现数据类型错误。
用户默认值是 iOS 开发中常用的一种简单数据存储方式,适合存储少量用户数据和应用程序状态。但对于大量数据存储,应考虑其他更适合的方案,如文件系统、Core Data 或数据库
- 可以使用用户默认启动标准作为内置实例来管理应用程序的默认设置,并将其附加到应用中。
- 对于更高级的应用,可以创建自定义的用户默认实例,以便在可能创建的多个应用扩展中共享默认设置。
- 创建一个单一的设置方法,接受几乎所有类型的数据(整数、布尔型、字符串等),并将字符串名称附加到此数据上以作为关键信息。用户默认值中进行读取时使用相同的键。
4.编码协议
编码协议是在 Swift 中用于实现自定义对象序列化和反序列化的协议。在 Swift 中,常用的编码协议是 Codable 协议,它是一个组合协议,包括 Encodable 和 Decodable 两个子协议。
- Encodable 协议用于将自定义类型编码为外部表示形式,例如 JSON、Plist 等。
- Decodable 协议用于将外部表示形式解码为自定义类型。
通过遵循 Codable 协议,你可以实现自定义类型的编码和解码,以便在应用程序中方便地进行数据的持久化、网络传输等操作。
使用了JSONEncoder 将 user 对象编码为 JSON 格式的数据,然后将其保存到 UserDefaults 中。可以将用户数据持久化到本地存储中,以便在应用程序的不同部分之间共享用户数据。
2)解码
使用JSONDecoder类可以将JSON数据解码为Codable对象。
struct ContentView: View {
var body: some View {
Button("Decode JSON"){
//定义了包含 JSON 数据的字符串
let input = """
{
"name": "Taylor Swift",
"address": {
"street": "555,Talor Swift Avenue",
"city": "Nashville"
}
}
"""
//定义了一个遵循 Codable 协议的结构体 User,包含了用户的名称和地址信息
struct User: Codable{
var name: String
var address: Address
}
struct Address: Codable{
var street: String
var city: String
}
let data = Data(input.utf8)
//将 JSON 字符串转换为 Data 类型。
let decoder = JSONDecoder()
//创建了一个 JSON 解码器实例
//尝试使用 JSON 解码器解码 data 中的 JSON 数据为 User 结构体实例,并将结果赋值给 user 变量。如果解码成功,则打印用户的街道信息
if let user = try? decoder.decode(User.self,from: data){
print(user.address.street)
}
}
}
}
点击按钮后会尝试解析包含用户信息的 JSON 数据并打印用户的街道信息