内容简介: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))
}
}
}
复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
闪魂FLASH8网站建设实录
马谧铤 / 中国林业 / 2006-7 / 46.00元
《闪魂FLASH8网站建设实录》旨在提供以Flash(Flash 8.0为创作工具)为技术核心的整套互动网站的开发思路,其中包括了网站策划、平面设计、程序设计等实用的互联网应用技术。内容包括Photoshop CS2设计,FIash 8创作和ActionScript应用程序开发的操作流程。在技术学习的过程中.大家还将体会到顶级互动网站设计、网站建设的设计流程和思路。《闪魂FLASH8网站建设实录》......一起来看看 《闪魂FLASH8网站建设实录》 这本书的介绍吧!
UNIX 时间戳转换
UNIX 时间戳转换
RGB CMYK 转换工具
RGB CMYK 互转工具