内容简介:2019年 WWDC 大会上,苹果宣布了基于Swift语言构建的全新UI框架-SwiftUI。其界面布局完全抛弃了Storyboard和Autolayout,采用了声明式的界面语言(DSL,即Domain Specific Language),加上 Canvas 的实时预览功能,开发体验有了很大的提升。自 iOS SDK 2.0 开始,UIKIt已经伴随开发者近十年,其思想继承了成熟的 AppKit 和 MVC, 为iOS开发提高了良好的学习曲线。UIKit提供的是一套符合直觉的、命令式的编程方式,但是由于
2019年 WWDC 大会上,苹果宣布了基于Swift语言构建的全新UI框架-SwiftUI。其界面布局完全抛弃了Storyboard和Autolayout,采用了声明式的界面语言(DSL,即Domain Specific Language),加上 Canvas 的实时预览功能,开发体验有了很大的提升。
自 iOS SDK 2.0 开始,UIKIt已经伴随开发者近十年,其思想继承了成熟的 AppKit 和 MVC, 为iOS开发提高了良好的学习曲线。UIKit提供的是一套符合直觉的、命令式的编程方式,但是由于UIKit的基本思想要求 View Controller 承担绝大部分职责,它需要协调 model,view 以及用户交互,使得在较大型的项目里 View Controller 很臃肿,如果状态管理复杂甚至导致后期代码无法维护。近年来随着编程思想、技术的进步,越来越多的开始使用 声明式 或 函数式 的方式来进行界面开发,现在大热的 React 和Flutter 便是采取了声明式编程。
在这种情况下,今年发布的SwiftUI当然也采取了声明式编程。
什么是声明式编程
维基百科对命令式、声明式编程的描述如下:
声明式编程:是一种不使用控制流来表示计算逻辑的编程范式。 命令式编程:是一种通过语句来改变程序状态的编程范式。 复制代码
举个栗子,当我们要设置一个Text的时候,命令式编程如下
override func viewDidLoad() { super.viewDidLoad() let label = UILabel(frame: CGRect(x: 100, y: 100, width: 100, height: 100)) label.text = "世界和平" label.textColor = .purple view.addSubview(label) } 复制代码
使用SwiftUI声明式编程时,则:
var body: some View { Text("世界和平") .color(.purple) } 复制代码
从上面可以看出,UIKit命令式编程是通过一句句的代码来指导“要怎么构建UI”,而声明式编程则是使用各自的DSL来描述“UI应该是什么样子”
SwiftUI是怎么进行布局
在SwiftUI里上面Text的声明只是纯数据结构的描述,并不是实际显示出来的视图,SwiftUI 会直接读取 DSL 内部描述信息并收集起来,然后转换成基本的图形单元,最终交给底层 Metal 或 OpenGL 渲染出来。
在 SwiftUI 中Text属性的设置在内部都会用一个虚拟的 View 来承载,然后在开始布局的时候再进行布局计算,这样做的目的主要是方便底层在设计渲染函数时更容易做到 monomorphic call,省去无用的分支判断,提高效率。
官方教程小解
swiftUI控件
备注:要使用 SwiftUI 的完整功能, 需使用 Xcode 11, 并且将 macOS 系统升级到最新的10.15
这次SwiftUI的官方教程可以说苹果很给力,每一步代码都有讲解说明及效果图片,强烈推荐跟着教程敲一遍!这里简单说明一下教程中的一些细节
APP 的启动
在示例中,app在 AppDelegate
中通过 application(_:configurationForConnecting:options)
返回一个 UISceneConfiguration
实例:
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } 复制代码
这部分内容是 iOS 13 中新加入的通过 Scene
管理 app 生命周期的方式,以及多窗口支持部分所需要的代码。在 app 完成启动后,控制权被交接给 SceneDelegate
,它的 scene(_:willConnectTo:options:)
将会被调用,进行 UI 的配置:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let window = UIWindow(frame: UIScreen.main.bounds) window.rootViewController = UIHostingController(rootView: ContentView()) self.window = window window.makeKeyAndVisible() } 复制代码
这段代码则跟之前iOS app启动很类似,唯一不同的就是 rootViewController
为 UIHostingController
类型的, UIHostingController
是 UIViewController
的子类,主要负责接受一个SwiftUI的View的描述并将其用UIKit进行渲染,由于这种继承关系,所以可以做到将SwiftUI构建的界面可以集成到已有的UIKit app中,不需要从头开始就是基于SwiftUI的构建
some View
struct ContentView: View { var body: some View { Text("世界和平") .color(.purple) } } 复制代码
从 SceneDelegate
进入到 ContentView
,会发现一个奇怪的 some view
,要解释这个我们就需要先了解下 View
。
View
是SwiftUI的最核心的一个协议,代表了一个屏幕上元素的描述。这个协议中含有一个 associatedtype
:
public protocol View : _View { associatedtype Body : View var body: Self.Body { get } } 复制代码
这种带有 associatedtype
的协议不能作为类型来使用,而只能作为类型约束使用,即不能写成 var body: View
否则会报 Error
,提示 “Protocol 'View' can only be used as a generic constraint”
。
其实如果我们指明 Body 的类型,也是可以的,例如
struct ContentView: View { var body: Text { Text("世界和平") .color(.purple) } } 复制代码
但是有个问题就是如果每次修改Body的返回时,我们都需要收到调整相应的类型,会很麻烦也无必要。 some View
这种写法使用了 Swift 5.1 的 Opaque return types 特性。它向编译器作出保证,每次 body 得到的一定是某一个确定的,遵守 View 协议的类型,但是请编译器“网开一面”,不要再细究具体的类型。这一个编译期间的特性,在保证 associatedtype protocol
的功能的前提下,使用 some
可以抹消具体的类型。这个特性用在 SwiftUI 上简化了书写难度,让不同 View 声明的语法上更加统一。
Modifier
VStack(alignment: .leading) { Text("世界和平") .font(.title) } 复制代码
除了 View
之外, Modifier
是 SwiftUI 另一个重要概念。在上面的代码中, .font(...)
和 .foregroundColor(...)
都修饰了 Text()
视图的某些属性—— 字体和颜色
。每一个单独的 Modifier
并不会对 View
类型实例进行操作,而是一个返回 some View
类型的闭包。因此 Modifier
的运行机制与我们熟悉的 UIKit 中对视图属性进行修改的方式是相反的,我们构建出一个视图时并不会先初始化出一个 View 实例
再对其进行修饰,而是通过声明的各种 Modifier
构建出 View 实例
。
当容器内视图具有相同的属性,可以将其提取到容器外进行定义,从而减少大量的重复代码:
HStack(alignment: .top) { Text("世界和平") .font(.subheadline) Spacer() Text("地球无战事") .font(.subheadline) } .foregroundColor(.yellow) 复制代码
数据绑定
SwiftUI中一个很便捷的功能是视图与数据的绑定。在官方示例中, Toggle
控件就绑定了 showFavoritesOnly
,而且绑定数据的方式非常的优雅,在初始化方法中通过 $ 声明某个属性便可以让视图自动绑定此变量
注: State
是一个值或一组值,可以随时间发生变化并影响视图的行为、内容或布局。 :point_down:使用具有 @State
声明的属性将状态添加到视图。
@State var showFavoritesOnly = true var body: some View { Toggle(isOn: $showFavoritesOnly){ Text("Favorites only") } } 复制代码
ForEach
ForEach
能以与 List
相同的方式对集合进行,也就是说可以在任何使用子视图的地方使用它,例如堆栈、列表、groups中,当数据元素为简单的值类型,可以直接将 \.self
当做标识符的关键路径
struct LandmarkList_Previews: PreviewProvider { static var previews: some View { ForEach(["iPhone SE", "iPhone XS Max"].identified(by: \.self)) { deviceName in LandmarkList() .previewDevice(PreviewDevice(rawValue: deviceName)) } } } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Everything Store
Brad Stone / Little, Brown and Company / 2013-10-22 / USD 28.00
The definitive story of Amazon.com, one of the most successful companies in the world, and of its driven, brilliant founder, Jeff Bezos. Amazon.com started off delivering books through the mail. Bu......一起来看看 《The Everything Store》 这本书的介绍吧!