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) } } }