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方面的小细节,需要我们自己去处理下。

相关推荐
终端安全笔记8 小时前
iOS 27 之后「策略空转」:设备升级不报错,但旧策略不再管它
android·网络·安全·ios·智能手机
97650333511 小时前
iOS 上架 4.3a 被拒【uniapp专讲】
flutter·ios·objective-c·uniapp·swift
黑化旺仔1 天前
【OC】KVO
macos·ios·objective-c·cocoa
HouWan1 天前
SwiftUI 小技巧:如何读取 SwiftUI Font 的详细信息
ios·swiftui·apple
李游Leo1 天前
HarmonyOS 7 实战开发 05:把页面做到可上线状态
ios·harmonyos
终端安全笔记1 天前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
传奇开心果编程1 天前
【SwiftUI入门练中学】第1课 从零开始
学习·ui·ios·swiftui·swift
丿Wayne2 天前
iOS Token 刷新实战(Actor 避免并发重复刷新)
ios·swift·token·actor·swift并发
终端安全笔记2 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩2 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift