【WWDC2019 之 SwiftUI】02 - SwiftUI 开发必须要了解的知识

SwiftUI 是一个全新的框架,它是为了以最快的路径开发 App 而设计的。虽然它是全新的,但是它包含了大量常见的组件,如下图:

在 UIKit 时代, 我们需要花大量的时间来写基本的 UI;而有了 SwiftUI 之后,我们可以把更多的时间花在自定义功能和业务逻辑上,借用了苹果的图,对比如下:

为什么说 SwiftUI 简化基本 UI 的编写?这里以显示一组数据为例:1)在 UIKit 中,我们使用 UITableView,需要实现 UITableViewDataSource;2)而在 SwiftUI 中,只需要一个 List 就可以搞定。下面看一下 List的 demo:

struct ContentView : View {
    let ints = [1, 2, 3, 4, 5]
    
    var body: some View {
        List(ints) { int in
            Text("\(int)")
        }
    }
}

View

在 UIKit 中有 UIView,在 AppKit 中有 NSView,而在 SwiftUI 中也有类似的概念,叫做 View

在 View 的层级管理上,SwiftUI 有很大的优势。假设有以下层级关系:

在用 UIKit 实现时,我们需要先定义一堆的 UIView实例,然后再通过 addSubview() 去组成 View 的层级关系。而且从代码上看,我们很难一眼看出 View 之间的层级关系。但是在 SwiftUI 就不一样了,下面是 SwiftUI 的实现代码:

VStack {
    Text("Avocado Toast").font(.title)
    
    Toggle(isOn: $order.includeSalt) {
        Text("Include Salt")
    }
    Toggle(isOn: $order.includeRedPepperFlakes) {
        Text("Include Red Pepper Flakes")
    }
    Stepper(value: $order.quantity, in: 1...10) {
        Text("Quantity: \(order.quantity)")
    }
    
    Button(action: submitOrder) {
        Text("Order")
    }
}

在 SwiftUI 中,直接把 subview 放在 superview 初始化方法的一个 closure 参数里,而不需要再调用 addSubview(),View 之间的层级关系一目了然。

到这里,我们可以总结出:1)UIKit 的实现过程,其实是属于命令式编程。你需要一步步告诉程序做什么和怎么做,例如先把各种 view 初始化好,然后把这个 subview 添加到那个 superview 里面等;2)SwiftUI 的实现过程是声明式编程,你只需要告诉程序你需要什么、这个东西放置在哪里。举个简单的例子,假设你想让朋友做一个番茄炒蛋:命令式编程就像是你教朋友做番茄炒蛋,告诉他第一步把番茄洗干净,第二步把番茄切片 …… ;而声明式编程就是你直接告诉朋友,我要吃番茄炒蛋,就这么简单,至于怎么做就交给你朋友了。

Object Binding

从上面的代码我们看到有些传入的参数前面有 $$意思是我们传入的对象是一个 binding,而不是一个普通的对象。下面是 Binding 的语法:

struct OrderForm: View {
    @State private var order: Order
    
    var body: some View {
        Stepper(value: $order.quantity, in: 1...10) {
            Text("Quantity: \(order.quantity)")
        }
    }
}

Binding 属性被 @State 标记。当你看到一个属性携带着 $ 被传入,那么意味着它是一个 Binding,允许被另一个 View 更改。在这个例子中,说明 order.quantity 的值可以被 Stepper 内部修改。

Modifier

以上面讲解 View 的其中一行代码为例:

Text("Avocado Toast").font(.title)

前半部分 Text("Avocado Toast") 是一个 View,而后半部分 font(.title) 就是一个 Modifier。我们看一下这个 Modifier 的方法定义:public func font(_ font: Font?) -> Text ,很明显,Modifier 就是一个根据已经存在的 View 重新创建一个新的 View。另外还可以通过链式语法修改更多属性:

VStack {
    Text("Avocado Toast")
        .font(.title)
        .foregroundColor(.green)
        
       ...
}

上面的代码会创建这样一个 View 层级:

每添加一个 Modifier,View层级就会不断变得复杂。但是,SwiftUI 可以很熟练根据 View 层级把我们的 Views 进行渲染。所以即使我们把 Text包装在多个 Wrapper Views,SwiftUI 会在底层整理成一个高效的数据结构,然后被渲染系统使用。

当我们不需要担心性能时,你会发现链式语法语法可以给我们带来好处。

第一,Modifier Chains 可以以可视化的效果执行确定顺序。 我们来看这组代码:

Text("🍞🥑")
    .background(Color.green, cornerRadius: 12)
    .padding(.all)

Text("🍞🥑")
    .padding(.all)
    .background(Color.green, cornerRadius: 12)

他们的渲染结果分别为:

发现什么区别没?第一个例子看起来好像 padding Modifier 没有效果,但实际上是有的,只是周围的 padding 是白色背景而已。仔细分析代码,我们可以看到,第一个例子先执行 background 后执行 padding ,所以 backgroundpadding 没有任何作用;而第二个例子,先执行 padding 后执行 backgroundbackground 就能应用到 padding

如果把 backgroundpadding 定义为 Text 的属性,而不是独立的 Modifier,那么在不借助文档或者不断尝试运行代码的情况下,我们就无法知道哪个属性先应用到 Text 上。而通过链式语法把多个 Modifiers 链接在一起,就可以很明确知道对 Text 修改的顺序。

第二,Modifier 可以同时对多个 View 进行修改。例如:

我们可以把下面的代码:

VStack(alignment: .leading) {
    Toggle(isOn: $order.includeSalt) { ... }
        .opacity(0.5)
    Stepper(value: $order.quantity, in: 1...10) { ... }
        .opacity(0.5)
    Button(action: submitOrder) { ... }
        .opacity(0.5)
}

改为:

VStack(alignment: .leading) {
    Toggle(isOn: $order.includeSalt) { ... }
    Stepper(value: $order.quantity, in: 1...10) { ... }
    Button(action: submitOrder) { ... }
}
.opacity(0.5)

自定义 View

在使用 UIKit 时,所有的自定义 View,都需要继承自 UIView。在 SwiftUI 中,也是类似的思想,不同的是从 UIView (类)变成了 View (协议)。

我们仔细看一下 SwiftUI 中的 View定义:

public protocol View : _View {
    associatedtype Body : View
    var body: Self.Body { get }
}

不像 UIKit 中的 UIView存储了大量的属性,SwiftUI 中的 View 变得非常轻量化,只需要实现一个 body 属性即可,View 的属性全部通过 Modifiers 去修改。下面看一个官方视频中的一个自定义 View:

struct OrderHistory : View {
    let previousOrders: [CompletedOrder]
    
    var body: some View {
        List(previousOrders) { order in
            VStack(alignment: .leading) {
                Text(order.summary)
                Text(order.purchaseDate)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }
        }
    }
}

在 SwiftUI 中,所有的自定义 View 都是通过 struct 去定义的,并实现 View 协议即可。我们前面讲过,SwiftUI 的核心思想是声明式编程,所以 SwiftUI 中的 View 不再是一个我们持续修改并永久存在的对象,而更像是一个纯函数,外部给我怎样的数据,我就给你怎样的 View,数据一样,返回的 View 就一样,这也是为什么 View 协议只有一个只读属性 body的原因。

上面代码中的 List 是一个演示声明式编程如此强大的例子:当 previousOrders 发生变化时,SwiftUI 会把新数据和旧数据进行对比,然后根据数据的变化非常高效地更新 UI,并且有对应的删除和增加的动画。这一整个复杂的过程,无需写一行代码;而在 UIKit 中,我们需要写很多代码去应对数据的变化。

想要更详细了解文章的内容,可以点击查看下面的视频。想及时看到我的新文章的,可以关注我。

参考资料

SwiftUI Essentials - WWDC 2019 - Videos - Apple Developer

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341