DatePicker
DatePicker 为日期选择器,对应 UIKit 中的 UIDatePicker。我们需要绑定一个 Date 类型的变量来记录当前选择的日期。
struct ContentView: View {
@State private var birthDay: Date = Date() //绑定日期
var body: some View {
//第一个参数为绑定的参数,第二个参数为显示的日期内容
DatePicker(selection: $birthDay, displayedComponents: .date) {
Text("出生日期")
}.environment(\.locale, Locale(identifier: "zh_CN")) //默认为英文选择器,这里指定为中文
}
}
当然我们实际使用的时候要求大多不会那么简单,下面给一个较为全面的使用案例。
struct ContentView: View {
@State var selectedDate = Date()
//日期格式化
var formatter: DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
return dateFormatter
}
//日期选择范围
var dateClosedRange: ClosedRange<Date> {
let min = Calendar.current.date(byAdding: .day, value: -10, to: Date())!
let max = Calendar.current.date(byAdding: .day, value: 10, to: Date())!
return min...max
}
var body: some View {
VStack{
//传入多种参数构造日期选择器
DatePicker(
selection: $selectedDate,
in: dateClosedRange,
displayedComponents: [.hourAndMinute, .date], //显示日期内容
label: { Text("选择日期") }
).padding()
Text(formatter.string(from: selectedDate))
}
}
}
onTapGesture()
和 UIKit 类似,Text 和 Image 不能接收点击事件,但是我们可以通过 .onTapGesture 修饰符添加点击事件,类似 UIKit 中的 addGestureRecognizer 方法。
struct ContentView: View {
var body: some View {
Text("文本")
.onTapGesture {
print("单击")
}
}
}
struct ContentView: View {
var body: some View {
Image(systemName: "clock")
.onTapGesture(count: 2) {
print("双击")
}
}
}
SwiftUI View 的生命周期
在先前 UIKit 框架中,视图控制器 UIViewController 有很多 UIView 的生命周期函数,例如 viewDidLoad、viewDidAppear 等,而目前在 SwiftUI 中仅提供了 onAppear 和 onDisappear。
struct ContentView: View {
var body: some View {
Text("Hello SwiftUI")
.onAppear() { //Text("Hello SwiftUI")显示在屏幕上时触发
print("onAppear")
}
.onDisappear() { //Text("Hello SwiftUI")在屏幕上消失时触发
print("onDisappear")
}
}
}
Group
Group 是 SwiftUI 中新增的,它可以把很多的 View 组合成一个 View(类似 ppt 中的组合)。Group 本身没有效果,只是一个容器,且容器内的元素不能超过 10,否则会报错。
struct ContentView: View {
var body: some View {
VStack {
Group {
Text("1")
Text("2")
Text("3")
Text("4")
Text("5")
}
Group {
Text("6")
Text("7")
Text("8")
Text("9")
Text("10")
}
}
}
}
与 Group相同,VStack、HStack、ZStack、List也有 10个元素内容的限制。
不同于 VStack/HStack/ZStack,Group 是完全透明的。将 Group 嵌入 VStack 中时,它的行为就像 VStack 一样,按照垂直方向排列子 View;嵌入到 HStack 中时,按照水平方向排列子 View。
此外,Group 的 Modifier 会作用于里面每个 View。
struct ContentView: View {
var body: some View {
VStack {
Group {
Text("Hello")
Text("SwiftUI")
Image(systemName: "heart")
}
.foregroundColor(Color.red)
.padding()
}
}
}
ScrollView
ScrollView 对应 UIKit 中的 UIScrollView,用于处理滑动的 UI 逻辑。
struct ContentView: View {
var body: some View {
//参数1:滚动方向(此处为垂直滚动),参数2:是否显示滚动条,参数3:滚动内容
ScrollView(.vertical, showsIndicators: false, content: {
//放置具体滚动内容
Text("SwiftUI").padding(20)
Divider()
Rectangle()
.foregroundColor(.orange)
.frame(width: UIScreen.main.bounds.size.width * 0.5, height: 1500, alignment: .center)
Divider()
Text("Example")
})
}
}
struct ContentView: View {
let imageNames = ["img","img","img","img","img","img","img"] //图片资源
var body: some View {
//横向滚动
ScrollView(.horizontal, showsIndicators: false) {
HStack {
//展示一组圆角图片
ForEach(imageNames, id: \.self) { imageName in
Image(imageName)
.resizable()
.cornerRadius(15)
.frame(width: 100, height: 100)
}.padding(.trailing, 10)
}
}
}
}
NavigationView
NavigationView 对应 UIKit 中的 NavigationController,为 iOS 顶部的导航栏控件。导航的标题使用 .navigationBarTitle 设置,默认显示的是大标题样式,可以在构造函数中设置 displayMode: .inline 变为传统标题。
struct ContentView: View {
var body: some View {
NavigationView {
Text("SwiftUI")
.navigationBarTitle("标题")
// .navigationBarTitle("标题", displayMode: .inline)
}
}
}
navigationBarTitle一定要设置给 NavigationView中最外层的那个 View。
导航栏上的按钮我们同样可以用 navigationBarItems 来设置。
struct ContentView: View {
var body: some View {
NavigationView {
Text("SwiftUI")
.navigationBarItems(leading: Button("设置"){
}, trailing: Button("编辑"){
}).navigationBarTitle("标题", displayMode: .inline)
}
}
}
若我们不想在某个界面看到导航栏,可以设置 .navigationBarHidden(true) 将导航栏隐藏起来。
struct ContentView: View {
var body: some View {
NavigationView {
Text("SwiftUI")
.navigationBarTitle("标题")
.navigationBarHidden(true)
}
}
}
TabView
TabView 为标签栏控件,对应 UIKit 的 UITabBarController。每一个 tabItem 代表一个标签栏,可以设置图片和文字。
struct ContentView: View {
var body: some View {
TabView {
Text("微信")
.tabItem {
Image(systemName: "message")
Text("微信")
}
Text("通讯录")
.tabItem {
Image(systemName: "person.2")
Text("通讯录")
}
}
}
}
若我们要设置默认选中,则先要设置 tag。
struct ContentView: View {
@State var selected = 1
var body: some View {
TabView (selection: $selected){
Text("微信")
.tabItem {
Image(systemName: "message")
Text("微信")
}.tag(0)
Text("通讯录")
.tabItem {
Image(systemName: "person.2")
Text("通讯录")
}.tag(1)
}
}
}
TabView 嵌套 NavigationView
熟悉 UIKit 开发的朋友想必都写过 TabBarController 嵌套 NavigationController 的代码,因为这是大多数 App 的业务逻辑。下面我们就以微信为例,用 SwiftUI 来实现相同的逻辑。
struct ContentView: View {
//借助UIKit设置导航栏和标签栏颜色
init() {
UITabBar.appearance().barTintColor = UIColor.lightGray
//只针对inline模式有效
UINavigationBar.appearance().barTintColor = .green
}
var body: some View {
TabView {
NavigationView{
Text("微信").navigationBarTitle("微信", displayMode: .inline)
}
.tabItem {
Image(systemName: "message")
Text("微信")
}.tag(0)
NavigationView{
Text("通讯录").navigationBarTitle("通讯录")
}
.tabItem {
Image(systemName: "person.2")
Text("通讯录")
}.tag(1)
NavigationView{
Text("发现").navigationBarTitle("发现")
}
.tabItem {
Image(systemName: "safari")
Text("发现")
}.tag(2)
NavigationView{
Text("我").navigationBarTitle("我")
}
.tabItem {
Image(systemName: "person")
Text("我")
}.tag(3)
}.accentColor(Color(red: 34/255.0, green: 172/255.0, blue: 37/255.0))
}
}
如果我们要设置导航栏和标签栏颜色,目前仍需要借助于 UIKit 实现。
EmptyView
顾名思义,EmptyView 为空白 View,常用于占位。
struct ContentView: View {
var body: some View {
EmptyView()
}
}
需要注意,在不设置背景(不显示)的情况下,是不能够响应事件的。
struct ContentView: View {
var body: some View {
EmptyView()
.frame(width: 300, height: 300)
.background(Color.red)
.clipShape(Circle())
.onTapGesture {
print("onTap")
}
}
}
AnyView
AnyView 表示任意一个 View 的实例,常用于抹除具体的 View 类型。
前面我们讨论过,body 中只能返回一种类型的 View,但是可以用 AnyView 包装不同的 View 之后返回。
struct ContentView: View {
@State var isLogin: Bool = false
var body: some View {
if isLogin {
return AnyView(
Text("您已登录")
)
} else {
return AnyView(
Button(action: {
self.isLogin.toggle()
}, label: {
Text("您未登录")
})
)
}
} //若不用AnyView包装则会报错
}
ContextMenu
ContextMenu 用于创建弹出式菜单。在 iOS 中通过 3D Touch 或长按触发,在 macOS 中通过单击鼠标右键触发。
struct ContentView: View {
@State var backgroundColor: Color = Color.orange
var body: some View {
NavigationView {
ZStack {
backgroundColor //背景色
Text("SwiftUI")
}
.navigationBarItems(leading: Button("设置") {
print("点击了设置")
}.contextMenu {
//第一个
Button(action: {
self.backgroundColor = Color.blue
}) {
Text("更新")
Image(systemName: "pencil")
}
//第二个
Button(action: {
self.backgroundColor = Color.red
}) {
Text("删除")
Image(systemName: "trash")
}
//第三个
Button(action: {
self.backgroundColor = Color.green
}) {
Text("添加")
Image(systemName: "plus")
}
})
.navigationBarTitle("标题", displayMode: .inline)
}
}
}