SwiftUI:熟人

在本教程中,我们将学习如何使用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 { // Target color block VStack { Rectangle() Text("Match this color") } // Guess color block VStack { Rectangle() HStack { Text("R: xxx") Text("G: xxx") Text("B: xxx") } } } 

每个色块都有一个矩形。 目标(目标)颜色块在矩形下方具有一个“文本”视图,而选定的一个(“猜测”)具有三个“文本”视图。 稍后,我们将用滑块的实际值替换“ 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文档 ,确实有很多有用的东西

Source: https://habr.com/ru/post/zh-CN471918/


All Articles