概述
本文首要共享SwiftUI 完成TabBar的过程,将使用事例的方式进行说明。为什么先共享这个哪?是因为 tabbar 是整个项目结构的基础,项目后续的开发根本依赖tabbar。
剖析
从页面布局看,整个页面分成两部分,一个是tab区,一个是内容区,所以咱们在设计tab的时分就需求考虑这两部分,
主体结构
代码如下:
struct ContentView: View {
var body: some View {
VStack {
//这儿为内容区
HStack {
Text("home")
}
.background(.green)
Spacer()
//这儿为tab区
HStack {
Text("Text")
}
.frame(height: 100)
.background(.gray)
}
.ignoresSafeArea(edges: .bottom)
.background(.yellow)
}
}
看下作用:
发现是扁的没有充满全屏,咱们能够使用GeometryReader,这个控件官方界说为一个容器视图,将其内容界说为自己的巨细和函数,这个视图返回一个灵活的父布局的首选巨细,读起来有点别扭。 代码如下:
GeometryReader { geometry **in**
VStack {
HStack {
Text("home")
}
.background(.green)
Spacer()
HStack {
Text("Text")
}
//这儿能够依据自己需求更改高度
.frame(width: geometry.size.width, height: geometry.size.height/9)
.background(.gray)
}
.ignoresSafeArea(edges: .bottom)
.background(.yellow)
}
看下作用:
这样看起来大体的结构有了,咱们的主体结构就这样
TabBarIcon
TabBarIcon一般由两部分组成,一个是图片,一个是案牍 代码如下:
struct TabBarIcon: View {
var body: some View {
VStack {
Image(systemName: "chart.pie.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
Text("主页")
.font(.footnote)
.font(.system(size: 16))
}
}
}
看下作用:
放到主页面中看下:
发现布局不对,并且图片与案牍也是写死的,这样咱们让它活一下,增加 TabBarIcon 的宽高,图片,案牍。
代码如下:
let width, height: CGFloat
let systemIconName, tabName: String
var body: some View {
VStack {
Image(systemName: systemIconName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: width, height: height)
.padding(.top, 6)
Text(tabName)
.font(.footnote)
.font(.system(size: 16))
Spacer()
}
.padding(.horizontal, -2)
}
主页面也改一下
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
VStack {
HStack {
Text("home")
}
.background(.green)
Spacer()
ZStack {
HStack {
TabBarIcon(width: geometry.size.width/5, height: geometry.size.height/32,systemIconName: "chart.pie.fill", tabName: "主页").frame(maxWidth: .infinity)
TabBarIcon(width: geometry.size.width/5, height: geometry.size.height/32,systemIconName: "pencil.circle", tabName: "概况").frame(maxWidth: .infinity)
TabBarIcon(width: geometry.size.width/5, height: geometry.size.height/32,systemIconName: "person.crop.circle.fill", tabName: "我的").frame(maxWidth: .infinity)
}
// 将宽度设置为父视图的宽度巨细,高度需求微调,能够设置为详细是数值,比方 100
.frame(width: geometry.size.width, height: geometry.size.height/9)
.background(.gray)
}
.ignoresSafeArea(edges: .bottom)
.background(.yellow)
}
}
}
}
看下作用:
作用根本达到了,但是现在却还不能点击,也不能切换,也没有点击亮
切换与高亮
完成点击,这时分咱们就需求在 TabBarIcon中增加点击事件,同时点击后图片及案牍都变成高亮显现 为了方便办理及拓展性,咱们创立一个ViewRouter进行办理 代码如下:
enum Page {
case home
case detail
case mine
}
class ViewRouter: ObservableObject {
@Published var currentPage: Page = .home
}
修正TabBarIcon中代码 代码如下:
struct TabBarIcon: View {
@StateObject var viewRouter: ViewRouter
let assignedPage: Page
let width, height: CGFloat
let systemIconName, tabName: String
var body: some View {
VStack {
Image(systemName: systemIconName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: width, height: height)
.padding(.top, 6)
Text(tabName)
.font(.footnote)
.font(.system(size: 16))
Spacer()
}
.padding(.horizontal, -2)
.onTapGesture {
//点击把当前页付值给viewRouter
viewRouter.currentPage = assignedPage
}
//这俩修正高亮
.foregroundColor(viewRouter.currentPage == assignedPage ? .green : .white)
}
}
修正主页代码 代码如下:
struct ContentView: View {
@StateObject var viewRouter: ViewRouter = ViewRouter()
var body: some View {
GeometryReader { geometry in
VStack {
HStack {
Text("home")
}
.background(.green)
Spacer()
ZStack {
HStack {
TabBarIcon(viewRouter: viewRouter, assignedPage: .home, width: geometry.size.width/5, height: geometry.size.height/32,systemIconName: "chart.pie.fill", tabName: "主页").frame(maxWidth: .infinity)
TabBarIcon(viewRouter: viewRouter, assignedPage: .detail, width: geometry.size.width/5, height: geometry.size.height/32,systemIconName: "pencil.circle", tabName: "概况").frame(maxWidth: .infinity)
TabBarIcon(viewRouter: viewRouter, assignedPage: .mine,width: geometry.size.width/5, height: geometry.size.height/32,systemIconName: "person.crop.circle.fill", tabName: "我的").frame(maxWidth: .infinity)
}
// 将宽度设置为父视图的宽度巨细,高度需求微调,能够设置为详细是数值,比方 100
.frame(width: geometry.size.width, height: geometry.size.height/9)
.background(.gray)
}
.ignoresSafeArea(edges: .bottom)
.background(.yellow)
}
}
}
}
看下作用:
这时点击,咱们发现tab已经能够点击了,同时点击也有了高亮显现。那怎样去切换页面呢?
切换页面
首先咱们创立三个页面 Home, Mine, Detail,然后 在内容区进行切换 代码如下:
switch viewRouter.currentPage {
case .home:
Home()
case .detail:
Detail()
case .mine:
Mine()
}
查看作用:
页面已经能够正常切换,但是问题又来了,咱们怎样去push一个页面呢?
完成Push
在SwiftUI中有一个控件NavigationView, 这个控件是专门用来完成push作用的,咱们把它包裹住整个内容区域
代码如下:
**struct** ContentView: View {
@StateObject **var** viewRouter: ViewRouter = ViewRouter()
**var** body: **some** View {
GeometryReader { geometry **in**
NavigationView {
//内容区代码
VStack {...}
.ignoresSafeArea(edges: .bottom)
}
}
}
}
以概况页为例,怎样详细的去push到一个新页面,创立一个新页面PushPage,然后在detai里写push逻辑。
代码如下:
struct Detail: View {
var body: some View {
VStack {
NavigationLink {
PushPage()
} label: {
Text("push")
}
}
.navigationBarTitle("概况页", displayMode: .automatic)
}
}
查看下作用:
点击push发现能够正常push,同时底部tab也会自动隐藏。
结语
到这儿,咱们的TarBar作用就根本完成了,其中有些UI方面的小细节,需求咱们自己去处理下。