SwiftUI-学习
1. SwiftUI-第一天
1. 1SwiftUI的基本架构
这里介绍下SwiftUI中特有的两个文件,至于其他的文件大家都很熟悉了,这里就不多做介绍了。
- ContentView.swift 包含程序的初始用户界面(UI),是我们在此项目中执行所有工作的地方
- Preview Content 是一个黄色文件夹,其中包含Preview Assets.xcarets —— 这是另一个资源目录,特别是您在设计用户界面时要使用的图像,以便让您了解它们在程序运行时的外观
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
在我们开始编写自己的代码之前,有必要过一下我们没有见过的新功能
-
import SwiftUI
告诉Swift我们希望使用SwiftUI框架提供的所有功能。苹果为我们提供了很多框架,比如机器学习、音频播放、图像处理等等,所以我们不想假设我们的程序想要使用所有的东西,而是说我们想使用哪些部分,以便加载它们。 -
struct ContentView: View
创建一个名为ContentView的新结构体,表示它遵循View
的协议.View
来自SwiftUI
,它是您想在屏幕上绘制的任何内容都必须采用的基本协议——所有文本、按钮、图像等都是视图,包括您自己的布局,可以组合其他视图。 -
var body: some View
定义了一个计算属性body
,它有一个有趣的类型:some View
。这意味着它返回符合视图协议的内容。但额外的some
关键字添加了一个重要限制:它必须始终是被返回的同一类型的视图——您不能有时返回一种类型的内容,有时又返回另一种不同类型的内容。
View
协议只有一个要求,即您有一个名为body
的计算属性,该属性返回some View
。您可以(也将会)向视图结构中添加更多的属性和方法,但body
是唯一一个必须要有的。 -
Text(“Hello World”)
使用字符串“Hello World”创建一个文本视图。文本视图是绘制到屏幕上的简单静态文本,并将根据需要自动换行。
在ContentView
结构体下面,您将看到一个ContentView_Previews
结构体,它遵循PreviewProvider
协议。这段代码实际上不会构成最终应用程序的一部分,而是专门供Xcode
使用,以便它可以在代码旁边显示UI设计的预览。
1.2 创建表单
许多应用程序都要求用户输入某种内容——可能是要求他们设置一些偏好,可能是要求他们确认他们想要车去哪里接他们,可能是从菜单上订购食物,或是其他类似的东西。
SwiftUI为此提供了一个专用的视图类型,称为Form
.表单是正在滚动静态控件列表像文本和图像,但是也可以包括文本字段、切换按钮、按钮等用户交互控件
//创建一个结构体,遵守View的协议,view是所有屏幕显示的内容的基本协议
struct ContentView: View {
var body: some View {
//这个必须返回同一类型的视图
Form {
//创建一个文本视图,内容为Hello, World!
// Text("Hello, World!")
//SwiftUI中 一个父视图中,最多只能有10个子视图(是直接子视图,不包括子视图中的子视图)
//如果想要解决这个问题 我们可以使用Group来将解决
//相当于表哥头视图 和尾视图 通过这种方式可以将表单拆分成多个部分
Section {
Text("Hello,dasdsadsa")
}
//如果需要再表单中包含超过10以上的内容,可以使用Group来分组,Group不会改变用户界面的外观,它们只是让我们绕过了SwiftUI在父视图中只包含10个子视图的限制
Group {
Text("Hello, Worlssssd!")
}
Group {
Text("Hello, World!")
}
//如果希望表单在将其项拆分为块时看起来不同,则应改用Section视图。这会将表单拆分为不同的可视组,就像设置应用程序所做的那样
Section {
Text("footer")
Text("Hello, World!")
}
}
//这样直接发安徽两个文本内容k容器是错误的,需要被包裹在一个容器中 入Form表单
// Text("Hello, World!")
// Text("Hello, world!")
}
}
- 注意:
在SwiftUI中,一个父级中有10个子级的限制实际上适用于所有地方。
1.3 添加导航栏
- 我们可以仿造添加一个表单一样添加一个导航栏视图(
NavigationView:导航栏视图
)
struct ContentView: View {
var body: some View {
//以同样的方式 防止一个导航栏视图 swiftUI中 NavigationView
NavigationView {
Form {
Section {
Text("hello World!")
}
}.navigationBarTitle("SwiftUI导航栏").navigationBarItems(leading: Text("左侧lable")) //链式语法来赋值
//参考页面 iphone手机设置页面
// navigationBarTitle("SwiftUI导航栏", displayMode: .inline) //使用一个小的标题
//navigationBarTitle("SwiftUI导航栏")
//当我们将.navigationBarTitle()修饰符附加到表单时,Swift实际上会创建一个新表单,该表单具有导航栏标题和您提供的所有现有内容。
//因此使用大标题是很常见的
}
}
}
1.3 修改程序状态
- 创建一个按钮点击事件来改变属性状态
struct ContentView: View {
/// 定义一个储存属性
var tapCount = 0
@State private var tapCountTwo = 0
var body: some View {
//如果和在SwiftUI上 修改状态 - 例子 : 创建点击事件按钮,修改变量的值
//使用闭包的方式 创建一个带点击事件的按钮
Button("Tap Count:\(tapCountTwo)") {
//self.tapCount += 1
//这样看起来很合理 由于contenView是个stuct,如果修改结构体中的储存属性,需要加上mutating,例如 mutating var body: some View,但是swift中不允许我们创建一个可变的计算属性
// 根据这种情况 SwiftUI中为我们提供一个@State 属性包装器 @State允许我们绕过结构体的限制:我们知道不能更改它们的属性,因为结构是固定的,但是@State允许SwiftUI将该值单独存储在可以修改的地方。
self.tapCountTwo += 1
}
}
}
注意
:@State
允许我们绕过结构体的限制:我们知道不能更改它们的属性,因为结构是固定的,但是@State
允许SwiftUI
将该值单独存储在可以修改的地方。提示:
:@State
是专门为存储在一个视图中的简单属性而设计的。因此,苹果建议我们向这些属性添加私有访问控制,比如:@State private var tapCountTwo = 0
。
1.4 状态绑定到UI控件
- 如果把一个属性和控件实现双向绑定,控件可以读取也可以修改属性值, SwiftUI中使用
$
来实现双向绑定
struct ContentView: View {
//如果实现属性和 UI控件的绑定 使用的是$符号,如果属性前不使用$符号,就表示读取属性,并不形成双向绑定,也不能修改属性
@State private var name = ""
var body: some View {
Form {
TextField("Enter your name", text: $name)
//这里我们没有使用$符号,意味着这里我们不需要使用双向绑定, 我们想读取值,但是我们不想已某种方式将其写会,因为文本视图不会改变
Text("Your name is: \(name)")
}
}
}
1.5 在循环中创建视图
- SwiftUI为此提供了一个专用的视图类型,称为
ForEach
。这可以在数组和范围上循环,根据需要创建尽可能多的视图。更妙的是,ForEach
不会像我们手动输入视图一样被10个视图限制所影响。
struct ContentView: View {
var body: some View {
// 使用循环创建多个窗体 可以绕过手动创建视图不能超过10个的限制影响
Form {
// ForEach(0..<100) { index in
// Text("\(index)")
// }
//ForEach 使用闭包,我们可以对参数名使用速记语法
ForEach(0..<100) {
Text("\($0)")
}
}
}
}
ForEach
在使用SwiftUI
的Picker
视图时特别有用,它允许我们显示各种选项供用户选择。- 我们定义一个视图:
- 有一系列可能的学生名称
- 具有一个@State属性存储当前选定学生。
- 创建一个Picker视图,要求用户选择他们最喜欢的,并将选择的值和@State属性双向绑定。
- 使用ForEach循环遍历所有可能的学生姓名,将其转换为文本视图。
struct ContentView: View {
//常量不需要@State修饰 ,因为常量不会改变
let student = ["Hanry","hermione","Ron"]
@State private var currentStudent = 0
var body: some View {
VStack {
//原来的PiackerView
//Picker与currentStudent有双向绑定,这意味着它将开始显示0的选择,但是在用户滑动选择器时更新属性。
Picker("Selected your Student", selection: $currentStudent) {
ForEach(0..<student.count) { index in
Text(self.student[index])
//Text(self.student[$0])
}
}
Text("Your choose :Student #\(student[currentStudent])")
}
}
}
SwiftUI-第二天
- 今天又4个主题需要解决,您将在其中运用有关
form
、@State
、Picker
等知识
- 使用TextField读取用户的输入
- 在表单中创建选择器
- 使用分段控件选择百分比
- 计算每个人的金额
- 示例代码:
struct ContentView: View {
//1.我们构建一个账单分割应用程序,这意味着用户需要输入他们的账单话费、多少人分担,以及他们想留下的消费, 所以我需要声明3个可以修改的属性
@State private var currentMonery = "" //SwiftUI必须使用字符串来存储TextField输入值。
@State private var numberOfPeople = 2
@State private var tipPercentage = 2 //百分比数组的下标
let tipPercentages = [10,15,20,25,0] //百分比 例子10 代表想留下10%的消费
//定义一个计算属性,计算每个人最终应付的钱数
var totolPerPerson: Double {
let perpleCount = Double(numberOfPeople+2)//用餐人数
let tipSelection = Double(tipPercentages[tipPercentage])//给小费的%比
let orderAmout = Double(currentMonery) ?? 0 //订单签署
let tipValue = orderAmout / 100 * tipSelection //小费
let grandTotal = orderAmout + tipValue //总钱数
let amoutPerPerson = grandTotal / perpleCount //每个人应该付的钱
return amoutPerPerson
}
var body: some View {
NavigationView {
Form {
/**
1.我们的文本输入框与currentMonery属性有双向绑定。
2.checkAmount属性用@State标记,它自动监视值的更改。
3.当@State属性更改时,SwiftUI将重新调用body属性(即,重新加载我们的UI)
4.因此,文本视图将获得currentMonery的更新值。
*/
Section {
//把文本框的值和currentMonery属性双向绑定 ,双向绑定之后会自动监听值的改变
TextField("Amount", text: $currentMonery)
.keyboardType(UIKeyboardType.decimalPad)
// $numberOfPeople 下标表示的值
Picker("Number of people", selection: $numberOfPeople) {
ForEach(2..<100) {
Text("\($0) people")
}
}
}
//设置分区头的内容
Section(header: Text("How much tip do you want to leave?")) {
Picker("Tip percentage", selection: $tipPercentage) {
ForEach(0 ..< tipPercentages.count) {
Text("\(self.tipPercentages[$0])%")
}
}.pickerStyle(SegmentedPickerStyle())//表示分段空控件
}
Section {
Text("$\(totolPerPerson)")
}
}.navigationBarTitle("WeSplit") //直接设置表单的标题
//很多人认为应该将修饰符附加到NavigationView的末尾,但是需要将其附加到表单的末尾。
//原因是导航视图能够在程序运行时显示许多视图,因此通过将标题附加到导航视图中的内容,我们允许iOS自由更改标题。
}
}
}