在本教程中,我们将学习如何使用View计划UI应用程序,并学习如何使用
State变量来修改UI。
预计阅读时间:25分钟。
SwiftUI允许我们完全忘记Interface Builder(IB)和情节提要。 IB和Xcode在Xcode 4之前是分开的应用程序,但是当我们编辑名称IBAction或IBOutlet且应用程序崩溃时,它们之间的“停靠”仍然可见。因此,IB对代码的更改一无所知。 或者,当我们为脚本或表格单元格设置标识符时,Xcode无法检查它们,因为它们是字符串。
SwiftUI来解救! 输入代码后,我们立即在视图中看到更改。 一方面的更改会导致另一方面的更新,因此它们总是相关的。 没有可能会误认的字符串标识符。 以上就是全部代码,但是比起我们使用UIKit编写的代码要小得多,因此更易于理解,编辑和调试。 告诉我,那不是很棒吗?
走吧
让我们在Xcode项目(
Shift-Command-N )中开始一个新项目,选择iOS▸Single
View App ,调用RGBullsEye,并确保选择
SwiftUI作为界面。
现在,
AppDelegate.swift分为两个文件:
AppDelegate.swift和SceneDelegate.swift ,SceneDelegate包含一个窗口:
SceneDelegate几乎与SwiftUI不相关,但以下行除外:
window.rootViewController = UIHostingController(rootView: ContentView())
UIHostingController为SwiftUI-view
ContentView创建一个视图控制器。
注意: UIHostingController允许我们将SwiftUI-view集成到现有应用程序中。 将Hosting View Controller添加到我们的情节提要中,并通过UIViewController在其上创建序列。 然后,在视图控制器代码上使用带有segue的Control-drag来创建IBSegueAction ,在这里我们将托管控制器设置为rootView -SwiftUI-view。
当应用程序启动时,窗口显示
ContentView的实例,该实例在文件
ContentView.swift中定义。 这是一个符合
View协议的结构:
struct ContentView: View { var body: some View { Text("Hello World") } }
这是ContentUI(
body )内容的SwiftUI声明。 现在有一个带有“ Hello World”文本的文本视图。
在ContentView_Previews的正下方,返回ContentView的实例。
struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
在这里,我们可以设置预览的测试数据。 但是,这个预览到底在哪里?
在代码之后,立即有一个上面的空白:

单击
恢复 ,稍等片刻,然后...

绘制我们的UI
熟悉的东西不可见-这是
Main.storyboard文件。 在查看预览的过程中,我们将在代码中使用SwiftUI创建我们的UI:我们能到达那里吗? 但是不用担心,我们不必编写数百行代码来创建视图。
SwiftUI是声明性的:声明您的UI应该是什么样的,SwiftUI会将所有这些转换为可以完成所有工作的高效代码。 Apple允许您创建尽可能多的视图,从而使代码简单明了。 尤其推荐带有参数的可重用视图-这类似于将代码分配到单独的函数中。 您稍后会自己做。
我们的应用程序将具有很多视图,因此一开始,我们将草绘文本视图作为存根。
用以下内容替换文本(“ Hello World”):
Text("Target Color Block")
如有必要,请单击“
恢复”以更新预览。
现在,在
预览中 命令单击此视图,然后选择
Embed in HStack :

请注意,您的代码也已更改:
HStack { Text("Target Color Block") }
复制并粘贴Text运算符,然后在我们的HStack中对其进行编辑。 请注意:我们不会用逗号分隔运算符,而是将它们各自写在新行上:
HStack { Text("Target Color Block") Text("Guess Color Block") }
因此,它会在预览中显示:

现在,通过将
HStack放在
VStack上,为滑块存根准备一个位置。 这次我们将
在我们的代码中对HStack进行
Command-Click :

选择“
嵌入VStack” ; 将出现一个新代码,但预览不会更改。 稍后,我们将在未来的色块下添加视图。
在HStack之后立即添加新行,单击工具栏上的
+以打开库,然后将
Vertical Stack拖动到新行:

如预期的那样,代码和预览均已更改:

完成UI草案上的工作,以使一切看起来像这样:
VStack { HStack { Text("Target Color Block") Text("Guess Color Block") } Text("Hit me button") VStack { Text("Red slider") Text("Green slider") Text("Blue slider") } }
在新的VStack中,稍后将出现三个滑块,以及滑块和色块之间的按钮。
我们继续致力于UI
现在让我们在SwiftUI中练习填充包含彩色块的HStack:
HStack {
每个色块都有一个矩形。 目标(目标)颜色块在矩形下方具有一个“文本”视图,而选定的一个(“猜测”)具有三个“文本”视图。 稍后,我们将用滑块的实际值替换“ xxx”。
使用变量'@State'
在SwiftUI中,我们可以使用常规变量,但是如果希望变量的更改影响UI,则可以将变量标记为
'@State' 。 在我们的应用程序中,我们选择颜色,以便影响所选颜色的所有变量均为'@State'变量。
在
主体声明之前,将以下
几行添加到
struct ContentView内部:
let rTarget = Double.random(in: 0..<1) let gTarget = Double.random(in: 0..<1) let bTarget = Double.random(in: 0..<1) @State var rGuess: Double @State var gGuess: Double @State var bGuess: Double
R,G和B的值介于0和1之间。我们使用随机值初始化所需的值。 我们还可以将选定的值初始化为0.5,但暂时不对其进行初始化,以显示在这种情况下需要执行的操作。
让我们再
深入到
结构ContentView_Previews ,该
结构 将为Preview初始化ContentView实例。 现在,初始化程序需要选定值的初始值。 像这样更改
ContentView() :
ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5)
当我们制作滑块时,在预览中它们的值将在中间。
我们还应该在
SceneDelegate中的
场景函数
(_:willConnectTo:options :)中修复
初始化程序-用以下内容替换
ContentView() :
window.rootViewController = UIHostingController(rootView: ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5))
加载应用程序时,滑块指针将位于中间。
现在将颜色修改器添加到目标矩形:
Rectangle() .foregroundColor(Color(red: rTarget, green: gTarget, blue: bTarget, opacity: 1.0))
.foregroundColor修改器使用随机生成的RGB值指定的颜色创建一个新的Rectangle视图。
同样,修改猜测矩形:
Rectangle() .foregroundColor(Color(red: rGuess, green: gGuess, blue: bGuess, opacity: 1.0))
R,G和B的值为0.5时,我们将获得灰色。
单击恢复,请稍等。

使视图可重用
首先,我们不会考虑重用,而只是为红色制作滑块。 在用于滑块的
VStack中,将Text(“红色滑块”)插头替换为此
HStack :
HStack { Text("0") .foregroundColor(.red) Slider(value: $rGuess) Text("255") .foregroundColor(.red) }
我们将文本视图中的文本颜色设为红色。 并且他们为Slider添加了默认值。 滑块的默认范围是0到1,这非常适合我们。
注意:我们知道滑块从0到1,文本标签为“ 255”,以方便习惯于表示0到255范围内的RGB值的用户。
但是,变量具有哪种
$图标? 我们知道
吗? 和! 当使用
可选选项时 ,现在还有
$ ?
尽管他是如此之小和不起眼,但他仍然非常重要。
RGuess本身只是一个
只读值。 但是
$ rGuess是一个绑定 ,当用户移动滑块时,我们需要它来更新所选颜色的矩形。
要了解差异,请在可预测的矩形下设置三个文本视图的值:
HStack { Text("R: \(Int(rGuess * 255.0))") Text("G: \(Int(gGuess * 255.0))") Text("B: \(Int(bGuess * 255.0))") }
在这里,我们仅使用值,而不更改它们,因此我们不需要$前缀。
等待预览更新:

彩色矩形会略微收缩以适合滑块。 但是滑块的文本标签看起来很乱-它们太压在边缘了。 让我们向HStack添加另一个修饰符-填充:
HStack { Text("0") .foregroundColor(.red) Slider(value: $rGuess) Text("255") .foregroundColor(.red) } .padding()
现在好多了!

在HStack红色滑块上
单击命令并选择
Extract Subview :

这与“
重构▸提取到函数”的作用相同,但适用于SwiftUI视图。
此时,将出现几条错误消息,不用担心,现在我们将对其进行修复。
将结果命名为
ColorSlider并将此代码添加到新视图主体前面的顶部:
@Binding var value: Double var textColor: Color
现在将
$ rGuess替换
为$ value,并将.red替换
为textColor :
Text("0") .foregroundColor(textColor) Slider(value: $value) Text("255") .foregroundColor(textColor)
让我们回到VStack中ColorSlider()的定义并添加我们的参数:
ColorSlider(value: $rGuess, textColor: .red)
使用红色滑块确保预览正常,并用绿色和蓝色滑块替换文本存根。 不要忘记在此处插入正确的参数:
ColorSlider(value: $gGuess, textColor: .green) ColorSlider(value: $bGuess, textColor: .blue)
单击
恢复以更新预览:

注意:您可能已经注意到经常需要单击Resume 。 如果您喜欢快捷方式,则可能会喜欢Option-Command-P 。
现在好东西! 在预览的右下角,单击
实时预览按钮:

实时预览使我们可以与预览进行交互,就好像应用程序正在模拟器上运行一样!
尝试移动滑块:

太好了! 我们进入最后阶段。 毕竟,我们想知道我们挑选颜色的程度如何?
显示警报
将滑块设置到所需位置后,用户按下“
命中我”按钮,此后将显示带有等级的
警报 。
首先,向ContentView添加一种方法来计算分数。 在变量@State和之间
正文添加此方法:
func computeScore() -> Int { let rDiff = rGuess - rTarget let gDiff = gGuess - gTarget let bDiff = bGuess - bTarget let diff = sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff) return Int((1.0 - diff) * 100.0 + 0.5) }
diff值只是三维空间中两点之间的距离,即用户的误差值。 要获得估算值,请从1减去diff,然后将其值减小到0-100范围。diff越小,估算值就越高。
然后,用以下代码替换
Text(“命中按钮”)存根:
Button(action: { }) { Text("Hit Me!") }
Button具有动作和标签,例如UIButton。 我们想要采取行动来触发警报视图。 但是,如果我们在操作按钮中创建警报,那么什么也不会发生。
相反,我们将Alert设置为ContentView的一部分,并添加Bool类型的'@State'变量。 然后,在希望警报显示在操作按钮中的位置,将此变量设置为true。 当用户隐藏“警报”时,该值将重置为false-以隐藏警报。
添加此“ @State”变量:
@State var showAlert = false
然后将此代码添加为操作按钮:
self.showAlert = true
自我对我们来说是必需的,因为showAlert在闭包内部。
最后,将alert修饰符添加到按钮,因此我们的按钮看起来完全像这样:
Button(action: { self.showAlert = true }) { Text("Hit Me!") } .alert(isPresented: $showAlert) { Alert(title: Text("Your Score"), message: Text("\(computeScore())")) }
我们将$ showAlert作为绑定传递,因为此变量的值将在用户隐藏警报时更改,并且此更改将导致视图更新。
SwiftUI具有用于Alert视图的简单初始化程序。 默认情况下,它具有“确定”按钮,因此我们甚至不需要将其设置为参数。
打开实时预览,移动滑块,然后按“命中我”按钮。 瞧!

现在有了实时预览,您不再需要iOS模拟器。 尽管有了它,您可以水平测试应用程序:

结论
您可以
在此处下载完成的出版物草案。
本教程仅介绍了SwiftUI,但现在您对Xcode的用于创建UI和预览的新功能以及如何使用'@State'变量更新UI有了印象。
为了简单起见,我们没有为RGB创建数据模型。 但是大多数应用程序使用结构或类创建其数据模型。 如果要跟踪SwiftUI中模型的更改,则它必须符合
ObservableObject协议并实现
willChange属性,该属性将通知您更改。
查看 Apple示例,尤其是
通过SwiftUI的数据流 。
为了使SwiftUI易于理解,您可以将SwiftUI视图添加到现有应用程序中。 查看有关如何快速轻松地执行此操作的示例。
请参阅
我的出版物 ,了解如何使用SwiftUI实现折叠/下拉列表。
最后,研究
SwiftUI的
文档 ,确实有很多有用的
东西 !