SwiftUI 是一种非常简单的创新方法,可以利用 Swift 的强大能力在所有苹果设备平台上构建用户界面。通过 SwiftUI,开发者仅使用一组工具和 API 就能为所有苹果设备构建用户界面。SwiftUI 使用易于阅读和编写的声明式 Swift 语法,可与新的 Xcode 设计工具无缝协作,使你的代码和设计完美同步。SwiftUI 自动支持动态类型、黑暗模式、本地化和可访问性,你的 SwiftUI 代码将成为你写过的最强大的 UI 代码。
目标
快速了解SwfitUI。
成果:实现一个列表,点击列表的item,跳转到对应的详情。
本文根据苹果官方教程整理代码在这里
首先回想一下在UIKit中如何实现:
- 创建一个带导航Navigation的controller,用来布局和Push下一个页面。
- 添加UITableView,并实现UITableView的两个代理方法,展示列表。
- 创建UITableViewCell,布局UILabel 和 UIImageView。
- 创建详情页面,布局地图、三个UIlabel。
- 在UITableViewDelegate的代理方法中Push到详情。
对iOS开发来说这太简单太熟悉不过了,但很多代码比较繁琐,控件的创建、布局等。虽然简单,但繁琐,浪费了很多本该多花在业务上的时间。
在SwiftUI中怎么实现呢?
在实现之前,先看一下所需要的组件,按照用途大致分为基础组件、布局组件和功能性组件,以及XCode11提供的新功能。
所需组件
基础组件
Text
用来显示文字 类似于UIKit中的UILabel
Image
用来显示图片 类似于UIKit中的UIImageView
Spacer
用来填充空白
布局组件
VStack
竖直摆放的组合组件HStack
水平摆放的组合组件List
用来展示列表 类似于UIKit中的UITableView
功能型组件
-
NavigationView
展示导航栏 类似于UINavigationBar
-
NavigationButton
类似于pushViewController:
方法
XCode11相关功能
预览
实时看到对页面的做出的修改
-
纯SwiftUI时,默认静态预览。
点击预览串口的Resume按钮。
如果没有显示预览窗口则按下图操作打开即可
- 预览包含UIView子类视图时,需要打开时时预览
点击可以切换时时预览和静态预览
拖放
command键 + 鼠标点击组件,可以方便的添加组件,设置组件属性等。
代码实现
创建列表
struct LandmarkList : View {
var body: some View {
//自定义显示的内容
List(0 ..< 5) { item in
Text("hello")
.font(.title)
}
}
}
使用List
组件可以快速的创建滑动列表,不需要设置代理,不需要实现协议方法就达到类似于UIKit中UITableView的效果。
Text
用来展示文字,通过.font
设置了字体大小。将它放入List
中,它就是列表的Item。
效果:
从工程Resources文件夹中找到资源文件,引入工程,里面包含了json数据、图片等。再引入Models文件夹中的Data.swift
和Landmark.swift
,这些主要是为了组件数据和Model,不是本文讨论的重点。下面会用到这些数据。
创建Item
这一步在UIKit中像自定义UITableViewCell,需要再其中添加一个图片和一个文字。
在SwiftUI中,没有UITableViewCell的概念,需要显示一行的时候,只需要使用HStack
组件,HStack
组件是一个组合组件,其中可以放 Text
、Image
等组件。
创建 LandmarkRow
struct LandmarkRow : View {
var landmark: Landmark
var body: some View {
HStack {
landmark.image(forSize: 50)
Text(landmark.name)
}
}
}
landmark.image(forSize: 50)
这个方法返回一个指定大小的图片
Text
显示地标名称。
HStack
将图片和文字组合在一行里面显示,并配置的有默认格式。
效果:
把它带入第一步创建的列表中,并引入数据。
struct LandmarkList : View {
var body: some View {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
效果:
列表已经显示出来了。
想想UIKit中的那堆代码,是不是暗爽?
创建详情页
从效果图中看到详情页有一个地图、一个圆形图片、几个显示地名、位置的文字。
从布局上看最下面两个水平的文字可以摆放在水平组件中,再和标题文字一起摆放在竖直组件中。
地图、图片、水平摆放的组件再一起摆放在竖直摆放组件中。
创建地图模块:
struct MapView : UIViewRepresentable {
var coordinate: CLLocationCoordinate2D
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
}
}
要在SwiftUI中添加非SwiftUI的组件,需要遵循UIViewRepresentable
协议,并实现协议方法。
创建圆角图片:
struct CircleImage : View {
var image: Image
var body: some View {
image
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 4)
.shadow(radius: 10)
)
}
}
创建详情页
struct LandMarkDetail : View {
var landmark : Landmark
var body: some View {
VStack {
MapView(coordinate: landmark.locationCoordinate).frame(height: 300)
CircleImage(image: landmark.image(forSize: 250))
.offset(y: -130)
.padding(.bottom, -130)
//三个文字
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
//下面两个文字
HStack {
Text(landmark.park)
.font(.subheadline)
Spacer()
Text(landmark.state)
.font(.subheadline)
}
}
.padding()
Spacer()
}
}
}
VStack
竖直组合组件,里面包含了MapView
和CircleImage
以及VStack
。
VStack
中包含了标题文字以及HStack
.
HStack
中包含了水平摆放的两个文字组件。
效果:
实现跳转
上面已经分别实现了列表页和详情页面,下面实现跳转。
UIKit中想要Push效果需要创建UINavigationController ,想要显示导航栏需要设置UINavigationBar,想要跳转需要在UITableView的代理方法中调用pushViewController:
方法。
修改上面创建的列表:
struct LandmarkList : View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
.navigationBarTitle(Text("Landmarks"), displayMode: .inline)
}
}
}
NavigationView
组件类似于UINavigationBar
,可以设置导航栏标题和模式。
NavigationButton
可以直接将跳转方法直接和列表展示绑定在一起,逻辑更清晰明了。
总结
了解过Flutter的同学对这个接受可能会很快。
没有了解过Flutter的同学需要转变一下页面布局思路。
SwiftUI对iOS开发同学来是一大福音,毕竟都9012年了,还在使用UIKit中这么原始的布局,实在是苦不堪言。
SwiftUI需要iOS13以上的系统,但目前公司开发APP都会支持一定的老版本系统,还得使用UIKit。全面使用SwiftUI预计还有一段时间。毕竟,还有很多公司没有使用Swift呢。