代码下载
创建新项目
要想在Xcode中预览画布中的视图或者与画布中的视图进行交互,要求Mac系统版本号不低于macOS Catalina 10.15
- 打开Xcode,在启动页面点击创建新工程或者在菜单中选择文件->新建->项目
- 在项目模板选择器中,选择iOS作为项目平台,选项单视图应用(Single View App)作为项目模板,并点击下一步(Next)
- 输入项目名称,选择SwiftUI作为用户界面的创建方式,并点击下一步(Next),在磁盘目录下选择一个位置用来存放新创建的工程项目
工程创建好并打开后,在文件导航器中,选择ContentView.swift文件,可以浏览一下SwiftUI视图的组成结构。默认情况下,SwiftUI的视图文件包含两个结构体(Struct) 第一个结构体遵循View协议,描述视图的内容和布局。第二个结构体声明为第一个视图的预览视图,Xcode15 将此结构图更改为 #Preview
。
在画布(Canvas)上,点击恢复(Resume)按钮可以显示预览视图,也可以使用快捷键Command+Option+P
在body属性内部,修改文字Hello World为其它的不同的文字,当你在改变代码的同时,预览视图也会实时的更新对应的内容变化
定制文本视图(Text View)
可以通过修改代码来改变一个视图的显示样式,也可以通过检查器获取视图可修改属性,然后再写对应的代码改变样式。在创建应用的过程中,可以同时使用源码编辑器、画布或者检查器,无论当前使用的是哪一个工具编辑视图,代码会保持和这些编辑器展示的样式一致
使用检查器来定制视图的显示样式
在代码中右键点击控件单词,会弹出一个编辑弹层,然后选择检查器(Inspect), 编辑弹层显示所有可以定制的视图属性,选中的控件不同,可以定制的属性集合也不相同
使用检查器把文字更改为Turtle Rock,改变字体修改器为Title
定制SwiftUI视图所调用的方法被称为视图修改器(Modifiers),修改器在原视图的基础上修改部分显示样式和属性,返回一个新的视图,这样就可以让多个修改器串连进行,形成水平方向的链式调用,或者垂直方向的堆叠调用
手动在代码中添加foregroundColor(.green) 属性修改器,就会把文字的颜色调整为绿色。代码是决定视图样式的根本,当我们使用检查器来改变或移除一个属性修改器时,Xcode也会在代码编辑器中同步改变或移除对应的修改器代码
从检查器中点击Color弹出菜单,选择继承(Inherited),让文字的颜色恢复成原来的黑色,Xcode会自动更新你的代码来反映视图的实际显示状况
使用栈来组合视图
创建SwiftUI视图就是在body属性中描述视图的内容、布局及行为,但body属性只返回单个视图,这时组合多个视图时可以把它们放入一个栈中,通过水平、垂直、前后嵌套多个视图完成视图组合,做为一个整体在body属性中返回
- 双击Text视图的初始化代码打开结构化编辑弹窗,然后选择把控件嵌套在垂直栈中(Embed in VStack),在栈中添加Text View控件可以从组件中直接拖进栈中完成
- 点击Xcode右上角的+号,托动一个Text控件到指定位置,代码立即就会在编辑器中补全
- 把Text视图的占位文本修改为Joshua Tree Nation Park,视图会自动调整位置布局
- 设置位置控件的字体为子标题样式
- 设置VStack初始化参数为左对齐内部的子视图。默认情况下,栈会把内部视图在自己的主轴上居中对齐,并自动计算各子视图的间距。下一步要添加一个Text控制用来描述公园的状态,它水平排列在位置信息的右边。
- 在弹出的菜单中选择嵌入到水平栈中(Embed in HStack)
- 在位置控件的后面加一个公园状态的Text视图,并把占位文字改为California,字体设置为子标题样式
- 为了水平布局使用整个屏幕宽度,在位置控件和公园状态控件中间添加一个Spacer控件,用来填充两个控件中间的空白部分,并把两个控件分别顶向屏幕的两侧。Spacer是一个可以伸缩的空白控件,他负责占用其它控件布局完成后剩下的所有空间
- 使用padding()修改器给地标信息内容视图整体加内边距
struct ContentView: View {
var body: some View {
VStack(alignment: HorizontalAlignment.leading) {
Text("Turtle Rock")
.font(.title)
HStack {
Text("Joshua Tree Nation Park")
.font(.subheadline)
Spacer()
Text("California")
}
}
.padding()
}
}
创建自定义图像视图(Image)
有了地标名称、地标位置及状态视图,下一步再添加一个地标图片视图。这个图片视图将自定义遮罩(mask)、边框(border)和阴影(shadow)
- 新建SwiftUI文件CircleImage
- 用Image替换Text,并使用turtlerock图片初始化Image视图
- 添加clipShape(Circle())修改器到Image,给图片添加圆形剪切效果。Circle是一个形状,它可以被用作遮罩、也可以是圆圈,还可以是圆形填充视图。
- 创建另一个灰色的圆圈并把它作为一个浮层添加到图片上,相当于给图片加了一个灰色边框
- 给视图添加半径为10的阴影
- 把圆形边框的颜色改成白色,就完成了自定义图片视图的创建
struct CircleImage: View {
var body: some View {
Image("turtlerock")
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
}
}
UIKit视图与SwiftUI视图混合使用
要创建一个地图视图,可以使用MapKit中的MKMapView视图类来渲染地图。要在SwiftUI中使用UIView及其子类,需要把这些UIView包裹在一个遵循UIViewRepresentable协议的SwiftUI视图中,SwiftUI中也包含适配WatchKit和AppKit的类似的协议。
- 选择文件->新建->文件,选择iOS平台,选择SwiftUI View模板,并点击下一步(Next),命名文件为MapView.swift,并点击创建(Create)
- 代码中导入MapKit引用,声明MapView遵循UIViewRepresentable协议。UIViewRepresentable协议要求实现两个方法用makeUIView(context:)和updateUIView(_:context:),第一个方法用来创建MKMapView,第二个方法用来配置视图响应状态变化
- UIViewRepresentable协议需要指定关联类型UIViewType为MKMapView
- 替换body,用makeUIView(context:)方法来代替,创建并返回一个空的MKMapView
- 创建方法updateUIView(_:context:),在方法内部设置地图视图的坐标为Turle Rock的中心。在静态模式下预览时,只会渲染swiftUI视图的部分,因为MKMapView是UIView的子类,所以需要切换到实时预览模式下才能看到地图被完全渲染出来
- 点击Live Preview(实时预览)按钮,可能需要点击Try Again和Resume按钮来激活预览模式的切换。切换到实时预览模式下不久就可以看到指定地标所在的地图位置了
struct MapView: UIViewRepresentable {
typealias UIViewType = MKMapView
func makeUIView(context: Context) -> MKMapView {
return MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(latitude: 34.011286, longitude: -116.166868)
let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
组合地标详情页
前面创建了个地标详情页所需要的各种子视图元素:名称、地点、圆形图片以及位置地图,现在可以把这些视图元素组合在一起形成地标详情页的整个视图
- 在项目工程浏览器中选择ContentView.swift文件
- body属性中嵌入一个VStack视图,它内部包含另一个VStack视图,内部的VStack视图又包含三个Text视图
- 在外层VStack的顶部添加自定义的地图视图MapView,并使用frame(width:height:)设置视图大小。当只指定高度时,宽度会自动计算为父视图的宽度,在这里就是屏幕宽度
- 点击Live Preview按钮进入实时预览模式,查看地图渲染情况。在实时预览模式下可以编辑视图,最新的改动也可以实时的刷新出来。
- 在MapView后面再添加一个CircleImage视图
- 为了让图片视图叠放在地图视图的上面,可以设置图片视图的垂直偏移量为-130,图片视图的底部内边距也为-130,这个效果就是把图片垂直上移了130,同时和下面的文字区域留出了130的空白分隔区
- 在外层VStack内部的最下面加上Spacer,可以让上面的视图内容顶到屏幕的上边
- 为了让地图的视图内容显示在状态栏的下方,可以给MapView添加edgesIgnoringSafeArea(.top)修改器,这可以让它在布局时忽略顶部的安全区域边距
struct ContentView: View {
var body: some View {
VStack {
MapView().frame( height: 300)
.edgesIgnoringSafeArea(.top)
CircleImage().offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: HorizontalAlignment.leading) {
Text("Turtle Rock")
.font(.title)
HStack {
Text("Joshua Tree Nation Park")
.font(.subheadline)
Spacer()
Text("California")
}
}
.padding()
Spacer()
}
}
}