• SwiftUI 2.0 课程笔记 Chapter 5


    课程链接:https://www.bilibili.com/video/BV1q64y1d7x5?p=5

    课程项目仓库:https://github.com/cnatom/MemorizeSwiftUI

    扩展extension

    swift可以通过extension关键字为现有的class, strut, enum或者protocol添加特性,我们以常用的Array结构体为例来说明。

    定义一个数组

    var cards: Array[Int] = [1]
    
    • 1

    我们想要为Array扩展一个新的计算变量oneAndOnly,只有当数组长度为1时,才会返回数组中唯一的变量,否则返回nil。

    一般写法如下:

    var oneAndOnly: Int? {
        if cards.count == 1 {
          return cards[0]
        } else {
          return nil
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们的可以通过扩展Array,使Array本身就带有上述功能。

    extension Array {
        var oneAndOnly: Element? { // 该计算变量的类型为泛型
            if self.count == 1 {
                return self.first // 返回第一个
            } else {
                return nil
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    扩展后,我们可以直接调用计算变量oneAndOnly,极大的简化了主业务代码。

    print(cards.oneAndOnly)
    
    • 1

    属性观察器Property Observers

    此处节选自菜鸟教程

    属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。

    可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。

    可以为属性添加如下的一个或全部观察器:

    • willSet在设置新的值之前调用
    • didSet在新的值被设置之后立即调用
    • willSet和didSet观察器在属性初始化过程中不会被调用
    class Samplepgm {
        var counter: Int = 0{
            willSet(newTotal){
                print("计数器: \(newTotal)")
            }
            didSet{
                if counter > oldValue {
                    print("新增数 \(counter - oldValue)")
                }
            }
        }
    }
    let NewCounter = Samplepgm()
    NewCounter.counter = 100
    NewCounter.counter = 800
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    以上程序执行输出结果为:

    计数器: 100
    新增数 100
    计数器: 800
    新增数 700
    
    • 1
    • 2
    • 3
    • 4

    View布局机制

    HStack与VStack

    Stack根据Views“灵活度”从低到高的顺序,依次提供空间

    举个例子说明什么是灵活度:

    • Image组件一般是固定大小,因此认为是“最不灵活的”。

    • Text组件总是想调整大小以完全适合其文本,因此认为是“稍微灵活的”。

    • RoundedRectangle组件总是使用提供的任何空间,因此被认为是“非常灵活的”。

    因此,Stack提供空间的先后顺序:Image、Text、RoundedRectangle。

    这个其实非常容易理解,RoundedRectangle想要霸占Stack中的所有剩余空间,当然需要等那些固定大小的View放在Stack里面才知道剩余空间有多大。因此,先为固定大小的View分配空间,再为RoundedRectangle分配空间。

    我们可以使用.layoutPriority(100.0)来更改组件的空间分配顺序,数值越大,优先级越高,分配次序越靠前。

    HStack{
      Text("我优先度被手动增大,先获得空间").layoutPriority(100.0)  // 分配次序:1
      Image("resource") // 分配次序:2      默认的layoutPriority为0  
      Text("我比Image灵活,我最后获得空间") // 分配次序:3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其他容器

    1. LazyHStackLazyVStack

      这类容器的大小与其包裹的子组件的大小一致,不会填充父容器的空间。

    2. ScrollView

      该容器会填充父容器的剩余空间,并提供一个可滚动的内部空间

    3. ZStack

      该容器根据子组件的大小调整容器大小。如果其子组件想要霸占所有剩余空间,那么ZStack也会霸占其父容器的剩余空间空间。

    4. 特殊的容器:修饰符

      修饰符本身会返回一个试图。如anyView.padding(10),生成一个内边距为10的包裹着anyView的容器

    布局实例

    HStack {
      	CardView(card: Card).aspectRatio(2/3, contentMode: .fit)
    }
    		.foregroundColor(Color.orange)
    		.padding(10)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. .padding(10)修饰符生成最外层容器。
    2. .foregroundColor(Color.orange)将其属性传入其内部
    3. HStack容器放在.padding(10)生成的容器内
    4. .aspectRatio(2/3, contentMode: .fit)生成一个宽度与HStack相同,纵横比为2/3的容器,放在HStack
    5. 最后将CardView组件放在.aspectRatio生成的容器中

    获取父组件大小

    使用SwiftUI提供的GeometryReader组件可以获取父组件的大小

    ......
    var body: some View{
        GeometryReader{ geometry in
            // geometry.size : 获取父组件的大小
            // geometry.size.height : 父组件的高度
            // geometry.size.width : 父组件的宽度
            Text(card.content).font(font(size: geometry.size)) // emoji的大小会根据容器宽度的大小变化
        }
    }
    ......
    private func font(size: CGSize) -> Font {
        Font.system(size: min(size.height,size.width) * 0.5)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    自定义View

    我们可以使用@ViewBuiler创建一个View列表

    比如一个返回类型为some View的函数

    @ViewBuilder
    func front(of card: Card) -> some View{
      	let shape = RoundedRectangle(cornerRadius: 20)
      	shape
      	shape.stroke()
      	Text(card.content)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    还可以创建一个struct视图

    struct MyContentView<Content: View>: View {
        let content: Content
    
        init(@ViewBuilder content: () -> Content) {
            self.content = content()
        }
    
        var body: some View {
            content
                .padding()
                .cornerRadius(10)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    【IDEA--dubug相关】Debug项目时启动不了(启动至一半卡住)
    JavaScript基础语法(1、基础知识)
    pico+unity3d运行测试方法
    从零安装pytorch并在pycharm中使用
    TCP协议,TCP报头及特点基础介绍
    mysql报错:Column Count Doesn‘t Match Value Count at Row 1
    MyBatisPlus(二十二)代码生成器
    gcc/g++的使用
    基于FPGA的图像指数对比度增强算法实现,包括tb测试文件和MATLAB辅助验证
    【linux驱动开发】-gpiolib概念与实践
  • 原文地址:https://blog.csdn.net/qq_15989473/article/details/125417929