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]

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

一般写法如下:

var oneAndOnly: Int? {
    if cards.count == 1 {
      return cards[0]
    } else {
      return nil
    }
}

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

extension Array {
    var oneAndOnly: Element? { // 该计算变量的类型为泛型
        if self.count == 1 {
            return self.first // 返回第一个
        } else {
            return nil
        }
    }
}

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

print(cards.oneAndOnly)

属性观察器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

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

计数器: 100
新增数 100
计数器: 800
新增数 700

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. 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. .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)
}

自定义View

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

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

@ViewBuilder
func front(of card: Card) -> some View{
  	let shape = RoundedRectangle(cornerRadius: 20)
  	shape
  	shape.stroke()
  	Text(card.content)
}

还可以创建一个struct视图

struct MyContentView<Content: View>: View {
    let content: Content

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        content
            .padding()
            .cornerRadius(10)
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值