SwiftUI学习分享 -使用SwiftUI实现TabBar

概述

本文主要分享SwiftUI 实现TabBar的过程,将使用案例的方式进行说明。为什么先分享这个哪?是因为 tabbar 是整个项目结构的基础,项目后续的开发基本依赖tabbar。

分析

从页面布局看,整个页面分成两部分,一个是tab区,一个是内容区,所以我们在设计tab的时候就需要考虑这两部分,

主体结构

代码如下:

scss 复制代码
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,这个控件官方定义为一个容器视图,将其内容定义为自己的大小和函数,这个视图返回一个灵活的父布局的首选大小,读起来有点别扭。 代码如下:

scss 复制代码
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一般由两部分组成,一个是图片,一个是文案 代码如下:

css 复制代码
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 的宽高,图片,文案。

代码如下:

scss 复制代码
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)
}

主页面也改一下

arduino 复制代码
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进行管理 代码如下:

arduino 复制代码
enum Page {
    case home
    case detail
    case mine
}

class ViewRouter: ObservableObject {
    @Published var currentPage: Page = .home
}

修改TabBarIcon中代码 代码如下:

scss 复制代码
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)
    }
}

修改主页代码 代码如下:

less 复制代码
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,然后 在内容区进行切换 代码如下:

arduino 复制代码
switch viewRouter.currentPage {
    case .home:
        Home()
    case .detail:
        Detail()
    case .mine:
        Mine()
}

查看效果:

页面已经可以正常切换,但是问题又来了,我们怎么去push一个页面呢?

实现Push

在SwiftUI中有一个控件NavigationView, 这个控件是专门用来实现push效果的,我们把它包裹住整个内容区域

代码如下:

less 复制代码
**struct** ContentView: View {

    @StateObject **var** viewRouter: ViewRouter = ViewRouter()

    **var** body: **some** View {

            GeometryReader { geometry **in**
                NavigationView {
                //内容区代码
                VStack {...}
                .ignoresSafeArea(edges: .bottom)
            }
        }
    }
}

以详情页为例,怎么具体的去push到一个新页面,创建一个新页面PushPage,然后在detai里写push逻辑。

代码如下:

css 复制代码
struct Detail: View {
    var body: some View {
        VStack {
            NavigationLink {
                PushPage()
            } label: {
                Text("push")
            }
        }
        .navigationBarTitle("详情页", displayMode: .automatic)
    }
}

查看下效果:

点击push发现可以正常push,同时底部tab也会自动隐藏。

结语

到这里,我们的TarBar效果就基本实现了,其中有些UI方面的小细节,需要我们自己去处理下。

相关推荐
DisonTangor3 小时前
苹果发布iOS 18.2首个公测版:Siri接入ChatGPT、iPhone 16拍照按钮有用了
ios·chatgpt·iphone
- 羊羊不超越 -3 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
2401_8658548819 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
HackerTom1 天前
iOS用rime且导入自制输入方案
ios·iphone·rime
良技漫谈1 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
2401_852403551 天前
高效管理iPhone存储:苹果手机怎么删除相似照片
ios·智能手机·iphone
星际码仔2 天前
【动画图解】是怎样的方法,能被称作是 Flutter Widget 系统的核心?
android·flutter·ios
emperinter2 天前
WordCloudStudio:AI生成模版为您的文字云创意赋能 !
图像处理·人工智能·macos·ios·信息可视化·iphone
关键帧Keyframe2 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
pb82 天前
引入最新fluwx2.5.4的时候报错
flutter·ios