
你好 我叫Renat,我正在iOS中开发订阅分析服务-Apphud。
如您所知,Apple在WWDC 2019上引入了其新的SwiftUI框架,该框架在将来被设计用来替代(或不可以替代)熟悉的UIKit。 SwiftUI允许您以声明式描述应用程序接口,并大大减少了代码量。
苹果已经介绍了一些有趣的英语教程 ,其中包括许多示例。 我将尝试以问答的形式谈论新框架。 所以走吧
开始之前
要使用SwiftUI,您需要下载Xcode 11 Beta 。 您还必须是注册的Apple开发人员。 拥有最新的macOS Catalina是可取的,但不是必需的。 没有它,Canvas将不可用。
因此,在Xcode 11 Beta中,创建一个新项目,并确保选中“ Use SwiftUI”。
问与答
Interface Builder到哪里去了?
SwiftUI不再需要Interface Builder-它已被Canvas取代, Canvas是与代码密切相关的交互式界面编辑器。 编写代码时,将自动生成其在画布中的可视部分,反之亦然。 非常方便,最重要的是安全。 现在,由于您忘记使用变量更新@IBOutlet
,因此您的应用程序不会@IBOutlet
。 在本文中,我们不会涉及canvas ,只会考虑代码。
应用程序启动是否更改?
是的,现在应用程序界面中的初始对象不是UIWindow
,而是新的UIScene
类(或其后代UIWindowScene
)。 然后将一个窗口添加到场景中。 这些更改不仅会影响SwiftUI,还会影响整个iOS 13。
创建项目时,您将看到AppDelegate , SceneDelegate和ContentView文件 。 SceneDelegate - UIWindowScene
类的委托,用于在应用程序中控制场景。 强烈让人联想到AppDelegate 。

SceneDelegate类在Info.plist中指定
在委托方法scene: willConnectTo: options:
创建一个窗口和一个根UIHostingController
,其中包含一个ContentView
。 ContentView
是我们的“主页”页面。 所有开发都将在此类中进行。
View与UIView有何不同?
打开ContentView.swift
,将看到一个ContentView容器声明。 如您viewDidAppear
viewDidLoad
viewDidAppear
中没有熟悉的viewDidLoad
或viewDidAppear
方法。 屏幕的基础不是UIViewController
,而是View 。 首先要注意的是ContentView
是一个接受View
协议的struct
。 是的, View
现在已经成为一种协议,并且非常简单。 您需要在ContentView
实现的唯一方法是描述变量body
。 您的所有子视图和自定义视图都必须接受View
协议,即必须具有body
变量。
什么是身体?
Body
是直接添加所有其他子视图的容器。 这有点类似于html页面中的正文 ,其中html页面是ContentView
。 Body
应该始终只有一个后代,以及任何接受View
协议的类。
struct ContentView: View { var body: some View { Text("Hello, world!") } }
不透明的返回类型是什么?
some TypeName
的构造是Swift 5.1的一项创新,称为不透明返回类型 。 它用于以下情况:对于我们来说不重要的是返回哪个对象,主要是它支持指定的类型,在这种情况下为View
协议。
如果我们只是简单地写var body: View
,那么这意味着我们应该返回View
。 Any
类也不起作用,因为我们必须执行类型转换操作( 使用 as!
运算符 )。 因此,他们在协议名称前面提出了some
特殊字词来表示不透明的返回类型 。 除了View
我们还可以返回Text
, Image
和VStack
,因为它们都支持View
协议。 但是必须有一个确切的元素:当尝试返回多个View
编译器将引发错误。

尝试将多个元素返回到主体时发生编译错误
方括号内的语法是什么,addSubview在哪里?
Swift 5.1引入了以声明式样式将对象分组为一个整体的功能。 这类似于闭包块中的数组,但是元素从新行枚举而没有逗号和return 。 该机制称为函数构建器 。
这在SwiftUI中被广泛使用。 他们基于Function Builder,使ViewBuilder
一个声明性的界面设计器。 使用ViewBuilder
我们不再需要为每个元素编写addSubview
只需在闭合块内的新行中列出所有View
。 SwiftUI将元素添加并分组到一个更复杂的父容器中。
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @_functionBuilder public struct ViewBuilder {
SwiftUI框架中的ViewBuilder广告
如何添加UILabel,UIImageView和其他元素?
元素的创建非常简单:每个View
需要从新行编写,并使用修饰符功能( view修饰符 )更改外观。 我们熟悉的修饰符和函数之间的区别在于,它们始终返回容器对象而不是void
。 因此,我们可以通过该点创建整个修饰符链。
var body: some View { VStack{ Text("World Time").font(.system(size: 30)) Text("Yet another subtitle").font(.system(size: 20)) } }
但是,并非所有控件和View在SwiftUI中都有它们的类似物。 以下是UIKit及其类似物的部分类列表:
UITableView
> List
UICollectionView
没有类似物
UILabel
> Text
UITextField
> TextField
UIImageView
> Image
UINavigationController
> NavigationView
UIButton
> Button
UIStackView
> HStack
/ VStack
UISwitch
> Toggle
UISlider
> Slider
UITextView
没有类似物
UIAlertController
> Alert
/操作ActionSheet
UISegmentedControl
> SegmentedControl
UIStepper
> Stepper
UIDatePicker
> DatePicker
屏幕之间如何导航?
导航控制器承担特殊的NavigationView
的角色。 只需将代码包装在NavigationView{}
。 并且过渡动作本身可以添加到特殊的NavigationLink
按钮中,该按钮可以按条件的DetailView
屏幕。
var body: some View { NavigationView { Text("World Time").font(.system(size: 30)) NavigationLink(destination: DetailView() { Text("Go Detail") } } }
如何模态表达新观点? 例如,这是使用工作表构造完成的:
Button(action: { print("Button Pushed") self.show_modal = true }) { Text("Present Modal") }.sheet(isPresented: self.$show_modal) { ModalView() }
如上所述, body
不仅可以返回View
的实例,还可以返回接受此协议的任何其他类。 这使我们有机会不DetailView
,甚至DetailView
Text
或Image
!
如何在屏幕上排列元素?
元素彼此独立排列,可以在VStack
内部垂直放置,在VStack
水平HStack
,在另一个ZStack
上方放置。 ScrollView
和ListView
也可供我们使用。 您可以交替并共享这些容器以获得任何网格元素。
通过将容器相互组合,可以得到带有大量附件的相当大的树。 但是,SwiftUI专门为此进行了优化,因此深度容器嵌套不会影响性能。 这在wwdc的视频中有说明 ( 从15:32开始 )。
var body: some View { NavigationView { VStack { NavigationLink(destination: LargeView(timeString: subtitle)) { Text("See Fullscreen") } Text("World Time").font(.system(size: 30)) } } }
如何显示导航栏?
仅仅声明NavigationView
不够的;您必须为导航栏指定导航标题和样式。
NavigationView { VStack{}.navigationBarTitle(Text("World Time"), displayMode: .inline) }
注意, navigationBarTitle
函数不是在NavigationView
上调用的,而是在其内部View
上调用的。 DisplayMode是一个参数,指示导航栏的样式:大或标准。
有viewDidLoad方法的类似物吗?
如果要在初始化View
时执行代码,可以通过添加onAppear {}函数来实现。 可以将OnAppear添加到任何View
,例如,添加到VStack
。 在此示例中,当容器出现在屏幕上时,将向服务器发出http请求。
struct ContentView : View { @State var statusString : String = "World Time" var body: some View { NavigationView { VStack { NavigationLink(destination:DetailView()) { Text("Go Detail") } Text(statusString).font(.system(size: 30)) }.onAppear { self.loadTime() } } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date())" } } } }
我们调用loadTime
函数,该函数从服务器请求当前时间并返回WorldTime
模型。 我们不会在NetworkService
类中循环学习,您可以查看已下载源代码的所有代码。 文章末尾的链接。
呈现变量var statusString
以便稍后分配当前时间。 该变量具有特殊的@State
属性。 他是什么意思
属性包装器或@State
是@State
?
Swift 5.1引入了所谓的属性包装器 (或属性委托 )。 在SwiftUI中, 属性包装器用于使用我们自己的变量(例如, Toggle开关的值)更新或绑定视图参数之一。
@State
属性是放置在变量声明之前的特殊属性。 这使我们无需附加代码即可自动跟踪属性更改。 在上面的示例中,一旦我们更新statusString值,文本“世界时间”将更改为当前日期。
要绑定值( Properties Binding ),我们可以在代码本身的变量名称之前指定一个特殊字符$
:
struct DetailsView: View { @State var changeToggle: Bool var body: some View { Toggle(isOn: $changeToggle) { Text("Change Toggle") } } }
通过更改开关的位置,变量的值也将更改。
属性包装器是SwiftUI的一个非常重要的组成部分,我只是顺便提到了它们。 要更详细地了解属性包装器,请在此处 (第37分钟), 此处 (第12分钟)和此处 (第19分钟)观看wwdc的视频。
如何向运行时添加视图?
立即值得注意的是,您无法随时按字面意义添加视图 。 SwiftUI是一个声明式框架,可呈现整个视图 。 但是,您可以在体内设置各种条件,并在它们改变时更新视图的状态。 在此示例中,我们使用最简单的@State – if
变量isTimeLoaded
。
struct ContentView : View { @State var statusString : String = "World Time" @State var isTimeLoaded : Bool = false var body: some View { NavigationView { VStack { if isTimeLoaded { addNavigationLink() } Text(statusString).font(.system(size: 30)).lineLimit(nil) }.navigationBarTitle(Text("World Time"), displayMode: .inline) }.onAppear { self.loadTime() } } func addNavigationLink() -> some View { NavigationLink(destination: Text("124!!!")) { Text("Go Detail") } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date().description(with: Locale.current))" self.isTimeLoaded = true } } } } struct DetailView : View { var timeString : String var body : some View { Text(timeString).font(.system(size: 40)).lineLimit(nil) } }
顺便说一句,您是否注意到addNavigationLink()
函数addNavigationLink()
单词return
? 这是Swift 5.1的另一项创新-适用于具有单个表达式的函数,现在可以选择编写return
。 但是你可以写。
结论
这只是SwiftUI问答的一部分。 我研究了一般性问题,希望本文能帮助初学者了解该框架的要点。 SwiftUI仍然是原始的,但无疑会得到改进。
逻辑问题是: 值得学习UIKit吗? 当然可以 。 UIKit是在iOS上进行编程的基础,并将进一步发展。 而且,许多SwiftUI组件都是UIKit的包装。 好吧,到目前为止,还没有用于SwiftUI的库,框架和Pod。 一切都必须由你自己编写。 因此,最好研究两种开发方法,这样您才能成为更有价值的开发人员。
您可以在此处下载项目源。
感谢您阅读本文的结尾。 希望您觉得它有用。
读什么?