
SwiftUI 中的场景
2022/08/29 Xcode 14 iOS 16 macOS 13 鉴于 Scene 比较特殊,难以在 Eul 中内置示例,笔者为其单独创建了一个样例工程,源码:Eul/SampleScene。 在《生命周期的演变》 中已经介绍过一些关于场景的概念了,下面逐一介绍 SwiftUI 内置的场景及其用法。WindowGroup这是最重要、最常用的场景,新建一个 SwiftUI 工程时,在 app 入口处就会看到如下代码:@main struct MyAppApp: App { var body: some Scene { WindowGroup { ContentView() } } } WindowGroup 在不同的平台表现不一:在 iOS、watchOS 仅支持单窗口,iPadOS、macOS 支持多窗口。 比如在 Mac 下创建新窗口(系统默认快捷键为 ⌘ + N):scene_1.png在 iPad 中的多窗口界面:scene_2.png在 app 入口处可以添加多个 scene,但只有第一个会被初始化并展示在界面上。比如示例中多个平台共用的 scene,在 app 启...

SwiftUI 中的场景
2022/08/29 Xcode 14 iOS 16 macOS 13 鉴于 Scene 比较特殊,难以在 Eul 中内置示例,笔者为其单独创建了一个样例工程,源码:Eul/SampleScene。 在《生命周期的演变》 中已经介绍过一些关于场景的概念了,下面逐一介绍 SwiftUI 内置的场景及其用法。WindowGroup这是最重要、最常用的场景,新建一个 SwiftUI 工程时,在 app 入口处就会看到如下代码:@main struct MyAppApp: App { var body: some Scene { WindowGroup { ContentView() } } } WindowGroup 在不同的平台表现不一:在 iOS、watchOS 仅支持单窗口,iPadOS、macOS 支持多窗口。 比如在 Mac 下创建新窗口(系统默认快捷键为 ⌘ + N):scene_1.png在 iPad 中的多窗口界面:scene_2.png在 app 入口处可以添加多个 scene,但只有第一个会被初始化并展示在界面上。比如示例中多个平台共用的 scene,在 app 启...

Swift 中的 Result builders
2022/08/30 Xcode 14 iOS 16 macOS 13 在以往的命令式编程中,通常这样构建视图:let label = UILabel() label.frame = self.view.bounds self.view.addSubview(label) 但 SwiftUI 采用的是声明式语法:VStack { Text("1") List { Text("") } } 显而易见,SwiftUI 比 UIKit 要简洁地多,我们可以轻松地将不同的视图组合成复杂的界面。这得益于 Result builders(结果构造器),它使 SwiftUI 成为了特定领域语言(DSL)。 结果构造器是 Swift 5.4 正式引入的新特性,它可以将一系列子对象组合成新的对象,这个新对象又可以作为子对象去构造更复杂的对象。它在 SwiftUI 中无处不在,如构建场景的 @SceneBuilder,构建视图的 @ViewBuilder,还有 Swift 5.7 新增的正则构造器 RegexBuilder 等。 下面通过一个简单的示例来演示结果构造器的基本使用,假设我们想赋予 UI...

Swift 中的 Result builders
2022/08/30 Xcode 14 iOS 16 macOS 13 在以往的命令式编程中,通常这样构建视图:let label = UILabel() label.frame = self.view.bounds self.view.addSubview(label) 但 SwiftUI 采用的是声明式语法:VStack { Text("1") List { Text("") } } 显而易见,SwiftUI 比 UIKit 要简洁地多,我们可以轻松地将不同的视图组合成复杂的界面。这得益于 Result builders(结果构造器),它使 SwiftUI 成为了特定领域语言(DSL)。 结果构造器是 Swift 5.4 正式引入的新特性,它可以将一系列子对象组合成新的对象,这个新对象又可以作为子对象去构造更复杂的对象。它在 SwiftUI 中无处不在,如构建场景的 @SceneBuilder,构建视图的 @ViewBuilder,还有 Swift 5.7 新增的正则构造器 RegexBuilder 等。 下面通过一个简单的示例来演示结果构造器的基本使用,假设我们想赋予 UI...

Swift 中的不透明类型、存在类型以及 some、any 关键字
Xcode 14 beta 3 Swift 5.7不透明类型、some 关键字some 关键字由 Swift 5.1 引入,它用来修饰某个协议,使之成为不透明类型。 不透明类型是隐藏类型信息的抽象类型,其底层的具体类型不可动态改变。 初次接触 SwiftUI 的读者会看到这样的代码:var body: some View { Text("Hello") } body 是不透明类型 some View,调用者只知其是一个遵循 View 协议的抽象类型,却不知其底层的具体类型(Text),因为不透明类型对调用者隐藏了类型信息。 这里的”不可见“是对调用者而言的,而编译器具有”透视“视角,它能够在编译期获取到不透明类型底层的具体类型(Text),并确保其底层类型是静态的。 如果在 body 内这样写:Bool.random() ? Text("Hello") : Image(systemName: "swift") 编译器能够诊断出 Text 和 Image 是不同的类型,因而抛出错误。假设 body 内部可以动态地改变其底层的具体类型,这意味着更多的内存占用和复杂计算,这会导致程序的...

Swift 中的不透明类型、存在类型以及 some、any 关键字
Xcode 14 beta 3 Swift 5.7不透明类型、some 关键字some 关键字由 Swift 5.1 引入,它用来修饰某个协议,使之成为不透明类型。 不透明类型是隐藏类型信息的抽象类型,其底层的具体类型不可动态改变。 初次接触 SwiftUI 的读者会看到这样的代码:var body: some View { Text("Hello") } body 是不透明类型 some View,调用者只知其是一个遵循 View 协议的抽象类型,却不知其底层的具体类型(Text),因为不透明类型对调用者隐藏了类型信息。 这里的”不可见“是对调用者而言的,而编译器具有”透视“视角,它能够在编译期获取到不透明类型底层的具体类型(Text),并确保其底层类型是静态的。 如果在 body 内这样写:Bool.random() ? Text("Hello") : Image(systemName: "swift") 编译器能够诊断出 Text 和 Image 是不同的类型,因而抛出错误。假设 body 内部可以动态地改变其底层的具体类型,这意味着更多的内存占用和复杂计算,这会导致程序的...

Swift 5.7 新特性
简化的可选绑定(SE-0345)对可选类型解包时无需显式绑定,写法更简洁:let s1: String? = "s1" if let s1 { print(s1) } guard let s1 else { exit(0) } print(s1) 更强大的类型推断默认表达式的类型推断(SE-0347)Swift 现在支持给泛型参数赋予默认值,并且能根据上下文推断泛型参数的具体类型。func compute<C: Collection>(_ values: C = [0, 1, 2]) { } compute([1, 2, 3]) // [Int] compute(["a", "b", "c"]) // [String] compute([1, "2", {}]) // [Any] 多语句闭包的类型推断(SE-0326)以前的多语句闭包必须写明参数和返回值类型:let _ = [-1, 0, 1].map { v -> String in if v < 0 { return "negative" } else if v > 0 { return "positiv...

Swift 5.7 新特性
简化的可选绑定(SE-0345)对可选类型解包时无需显式绑定,写法更简洁:let s1: String? = "s1" if let s1 { print(s1) } guard let s1 else { exit(0) } print(s1) 更强大的类型推断默认表达式的类型推断(SE-0347)Swift 现在支持给泛型参数赋予默认值,并且能根据上下文推断泛型参数的具体类型。func compute<C: Collection>(_ values: C = [0, 1, 2]) { } compute([1, 2, 3]) // [Int] compute(["a", "b", "c"]) // [String] compute([1, "2", {}]) // [Any] 多语句闭包的类型推断(SE-0326)以前的多语句闭包必须写明参数和返回值类型:let _ = [-1, 0, 1].map { v -> String in if v < 0 { return "negative" } else if v > 0 { return "positiv...
Swift 5.6 新特性
不可用条件(#unavailable)#available 用于根据不同的平台、版本进行条件编译:if #available(iOS 15, *) { // 通配符 * 表示 Apple 所有平台,如果是 iOS,则要求 >= 15 } else { // iOS 15 以下 } Swift 5.6 引入了 #unavailable ,它和 #available 的意思正好相反,下面的示例和上面示例中的 else 分支表达的意思是一样的。if #unavailable(iOS 15) { // iOS 15 以下 } 它也支持同时指定多个平台:if #unavailable(iOS 15, macOS 12) { // iOS 15 以下, macOS 12 以下 } 注意:使用 #unavailable 使不需要通配符,我们的目的是为了使用它具体指出不可用的环境,使用 * 会造成歧义。类型占位符(_、 _?)Swift 5.6 支持使用占位符 _ 或 _? 表示需要声明的类型,我们无需显示地指定类型,编译器会根据上下文自行推断。let complexType: [Int: _]...
Swift 5.6 新特性
不可用条件(#unavailable)#available 用于根据不同的平台、版本进行条件编译:if #available(iOS 15, *) { // 通配符 * 表示 Apple 所有平台,如果是 iOS,则要求 >= 15 } else { // iOS 15 以下 } Swift 5.6 引入了 #unavailable ,它和 #available 的意思正好相反,下面的示例和上面示例中的 else 分支表达的意思是一样的。if #unavailable(iOS 15) { // iOS 15 以下 } 它也支持同时指定多个平台:if #unavailable(iOS 15, macOS 12) { // iOS 15 以下, macOS 12 以下 } 注意:使用 #unavailable 使不需要通配符,我们的目的是为了使用它具体指出不可用的环境,使用 * 会造成歧义。类型占位符(_、 _?)Swift 5.6 支持使用占位符 _ 或 _? 表示需要声明的类型,我们无需显示地指定类型,编译器会根据上下文自行推断。let complexType: [Int: _]...