CustomTabBar 自定义选项卡视图

1. 用到的技术点

  1) Generics      泛型

  2) ViewBuilder   视图构造器

  3) PreferenceKey 偏好设置

  4) MatchedGeometryEffect 几何效果

2. 创建枚举选项卡项散列,TabBarItem.swift

import Foundation
import SwiftUI//struct TabBarItem: Hashable{
//    let iconName: String
//    let title: String
//    let color: Color
//}///枚举选项卡项散列
enum TabBarItem: Hashable{case home, favorites, profile, messagesvar iconName: String{switch self {case .home: return "house"case .favorites: return "heart"case .profile:   return "person"case .messages:  return "message"}}var title: String{switch self {case .home: return "Home"case .favorites: return "Favorites"case .profile:   return "Profile"case .messages:  return "Messages"}}var color: Color{switch self {case .home: return Color.redcase .favorites: return Color.bluecase .profile:   return Color.greencase .messages:  return Color.orange}}
}

3. 创建选项卡偏好设置 TabBarItemsPreferenceKey.swift

import Foundation
import SwiftUI/// 选项卡项偏好设置
struct TabBarItemsPreferenceKey: PreferenceKey{static var defaultValue: [TabBarItem] = []static func reduce(value: inout [TabBarItem], nextValue: () -> [TabBarItem]) {value += nextValue()}
}/// 选项卡项视图修饰符
struct TabBarItemViewModifer: ViewModifier{let tab: TabBarItem@Binding var selection: TabBarItemfunc body(content: Content) -> some View {content.opacity(selection == tab ? 1.0 : 0.0).preference(key: TabBarItemsPreferenceKey.self, value: [tab])}
}extension View{/// 选项卡项视图修饰符func tabBarItem(tab: TabBarItem, selection: Binding<TabBarItem>) -> some View{modifier(TabBarItemViewModifer(tab: tab, selection: selection))}
}

4. 创建自定义选项卡视图 CustomTabBarView.swift

import SwiftUI/// 自定义选项卡视图
struct CustomTabBarView: View {let tabs: [TabBarItem]@Binding var selection: TabBarItem@Namespace private var namespace@State var localSelection: TabBarItemvar body: some View {//tabBarVersion1tabBarVersion2.onChange(of: selection) { value inwithAnimation(.easeInOut) {localSelection = value}}}
}extension CustomTabBarView{/// 自定义 tabitem 布局private func tabView1(tab: TabBarItem) -> some View{VStack {Image(systemName: tab.iconName).font(.subheadline)Text(tab.title).font(.system(size: 12, weight: .semibold, design: .rounded))}.foregroundColor(localSelection == tab ? tab.color : Color.gray).padding(.vertical, 8).frame(maxWidth: .infinity).background(localSelection == tab ? tab.color.opacity(0.2) : Color.clear).cornerRadius(10)}/// 选项卡版本1private var tabBarVersion1: some View{HStack {ForEach(tabs, id: \.self) { tab intabView1(tab: tab).onTapGesture {switchToTab(tab: tab)}}}.padding(6).background(Color.white.ignoresSafeArea(edges: .bottom))}/// 切换选项卡private func switchToTab(tab: TabBarItem){selection = tab}
}extension CustomTabBarView{/// 自定义 tabitem 布局 2private func tabView2(tab: TabBarItem) -> some View{VStack {Image(systemName: tab.iconName).font(.subheadline)Text(tab.title).font(.system(size: 12, weight: .semibold, design: .rounded))}.foregroundColor(localSelection == tab ? tab.color : Color.gray).padding(.vertical, 8).frame(maxWidth: .infinity).background(ZStack {if localSelection == tab{RoundedRectangle(cornerRadius: 10).fill(tab.color.opacity(0.2)).matchedGeometryEffect(id: "background_rectangle", in: namespace)}})}/// 选项卡版本 2private var tabBarVersion2: some View{HStack {ForEach(tabs, id: \.self) { tab intabView2(tab: tab).onTapGesture {switchToTab(tab: tab)}}}.padding(6).background(Color.white.ignoresSafeArea(edges: .bottom)).cornerRadius(10).shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 5).padding(.horizontal)}
}struct CustomTabBarView_Previews: PreviewProvider {static let tabs: [TabBarItem] = [.home, .favorites, .profile]static var previews: some View {VStack {Spacer()CustomTabBarView(tabs: tabs, selection: .constant(tabs.first!), localSelection: tabs.first!)}}
}

5. 创建自定义选项卡容器视图 CustomTabBarContainerView.swift

import SwiftUI/// 自定义选项卡容器视图
struct CustomTabBarContainerView<Content: View>: View {@Binding var selection: TabBarItemlet content: Content@State private var tabs: [TabBarItem] = []init(selection: Binding<TabBarItem>, @ViewBuilder content: () -> Content){self._selection = selectionself.content = content()}var body: some View {ZStack(alignment: .bottom) {content.ignoresSafeArea()CustomTabBarView(tabs: tabs, selection: $selection, localSelection: selection)}.onPreferenceChange(TabBarItemsPreferenceKey.self) { value intabs = value}}
}struct CustomTabBarContainerView_Previews: PreviewProvider {static let tabs: [TabBarItem] = [ .home, .favorites, .profile]static var previews: some View {CustomTabBarContainerView(selection: .constant(tabs.first!)) {Color.red}}
}

6. 创建应用选项卡视图 AppTabBarView.swift

import SwiftUI// Generics      泛型
// ViewBuilder   视图构造器
// PreferenceKey 偏好设置
// MatchedGeometryEffect 几何效果/// 应用选项卡视图
struct AppTabBarView: View {@State private var selection: String = "Home"@State private var tabSelection: TabBarItem = .homevar body: some View {/// 默认系统的 TabView// defaultTabView/// 自定义 TabViewcustomTabView}
}extension AppTabBarView{/// 默认系统的 TabViewprivate var defaultTabView: some View{TabView(selection: $selection) {Color.red.ignoresSafeArea(edges: .top).tabItem {Image(systemName: "house")Text("Home")}Color.blue.ignoresSafeArea(edges: .top).tabItem {Image(systemName: "heart")Text("Favorites")}Color.orange.ignoresSafeArea(edges: .top).tabItem {Image(systemName: "person")Text("Profile")}}}/// 自定义 TabViewprivate var customTabView: some View{CustomTabBarContainerView(selection: $tabSelection) {Color.red.tabBarItem(tab: .home, selection: $tabSelection)Color.blue.tabBarItem(tab: .favorites, selection: $tabSelection)Color.green.tabBarItem(tab: .profile, selection: $tabSelection)Color.orange.tabBarItem(tab: .messages, selection: $tabSelection)}}
}struct AppTabBarView_Previews: PreviewProvider {static var previews: some View {AppTabBarView()}
}

7. 效果图:

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/106592.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java练习题-获取数组元素最大值

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;Java练习题 &#x1f4ac;个人格言&#xff1a;不断的翻越一座又…

Qt之给控件添加右键菜单

一、设置控件 在对应控件的属性中&#xff0c;将contextMenuPolicy设置为CustomContextMenu。 二、添加槽函数 在对应控件上右键选择槽函数customContextMenuRequested(QPoint)。 三、在槽函数中添加右键菜单 在槽函数中输入如下代码&#xff0c;添加右键菜单。 //右键菜单 …

Flutter 剪裁(Clip)

&#x1f525; ClipOval &#x1f525; 子组件为正方形时剪裁成内贴圆形&#xff1b;为矩形时&#xff0c;剪裁成内贴椭圆 裁剪纯色背景 ClipOval(child: Container(width: 300.w,height: 300.w,decoration: const BoxDecoration(color: Colors.red),),), 裁剪背景图片 裁剪前…

《Deep Residual Learning for Image Recognition》阅读笔记

论文标题 《Deep Residual Learning for Image Recognition》 撑起CV界半边天的论文Residual &#xff1a;主要思想&#xff0c;残差。 作者 何恺明&#xff0c;超级大佬。微软亚研院属实是人才辈出的地方。 初读 摘要 提问题&#xff1a; 更深层次的神经网络更难训练。 …

(vue3)大事记管理系统 文章管理页

[element-plus进阶] 文章列表渲染&#xff08;带搜索&到分页&#xff09; 表单架设&#xff1a;当前el-form标签配置一个inline属性&#xff0c;里面的元素就会在一行显示了 中英国际化处理&#xff1a;App.vue中el-config-provider标签包裹组件&#xff0c;意味着整个组…

【LeetCode高频SQL50题-基础版】打卡第6天:第31~35题

文章目录 【LeetCode高频SQL50题-基础版】打卡第6天&#xff1a;第31~35题⛅前言员工的直属部门&#x1f512;题目&#x1f511;题解 判断三角形&#x1f512;题目&#x1f511;题解 连续出现的数字&#x1f512;题目&#x1f511;题解 指定日期的产品价格&#x1f512;题目&am…

Java实现hack汇编器

Hack汇编语言是一种特定于计算机体系结构的汇编语言&#xff0c;使用Hack架构的机器码指令来编写程序。Hack是一种基于Von Neumann结构的计算机体系结构&#xff0c;由Harvard大学的Nand to Tetris项目开发出来&#xff0c;用于实现计算机硬件和软件。 Hack汇编语言主要用于在…

linux 内核中的pid和前缀树

前言&#xff1a; 写这个文章的初衷是因为今天手写了一个字典树&#xff0c;然后写字典树以后忽然想到了之前看的技术文章&#xff0c;linux kernel 之前的pid 申请方式已经从 bitmap 变成了 基数树&#xff0c;所以打算写文章再回顾一下这种数据结构算法 一、内核中pid的申请…

【学习笔记】minIO分布式文件服务系统

MinIO 一、概述 1.1 minIO是什么&#xff1f; MinIO是专门为海量数据存储、人工智能、大数据分析而设计的对象存储系统。&#xff08;早前流行的还有FastDFS&#xff09; 据官方介绍&#xff0c;单个对象最大可存储5T&#xff0c;非常适合存储海量图片、视频、日志文件、备…

java.sql.SQLFeatureNotSupportedException解决方法

使用MyBatis访问数据库查询数据时报错&#xff1a; Caused by: java.sql.SQLFeatureNotSupportedExceptionat com.alibaba.druid.pool.DruidPooledResultSet.getObject(DruidPooledResultSet.java:1771)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun…

03在命令行环境中创建Maven版的Java工程,了解pom.xml文件的结构,了解Java工程的目录结构并编写代码,执行Maven相关的构建命令

创建Maven版的Java工程 Maven工程的坐标 数学中使用x、y、z三个向量可以在空间中唯一的定位一个点, Maven中也可以使用groupId,artifactId,version三个向量在Maven的仓库中唯一的定位到一个jar包 groupId: 公司或组织域名的倒序, 通常也会加上项目名称代表公司或组织开发的一…

JDBC操作BLOB类型字段

JDBC中Statement接口本身不能直接操作BLOB数据类型 操作BLOB数据类型需要使用PreparedStatement或者CallableStatement(存储过程) 这里演示通过PreparedStatement操作数据库BLOB字段 设置最大传入字节 一般是4M 可以通过以下命令修改 set global max_allowed_packet1024*1…

网页在线打开PDF_网站中在线查看PDF之pdf.js

一、pdf.js简介 PDF.js 是一个使用 HTML5 构建的便携式文档格式查看器。 pdf.js 是社区驱动的&#xff0c;并由 Mozilla 支持。我们的目标是为解析和呈现 PDF 创建一个通用的、基于 Web 标准的平台。 pdf.js 将 PDF 文档转换为 HTML5 Canvas 元素&#xff0c;并使用 JavaScr…

Puppeteer结合测试工具jest使用(四)

Puppeteer结合测试工具jest使用&#xff08;四&#xff09; Puppeteer结合测试工具jest使用&#xff08;四&#xff09;一、简介二、与jest结合使用&#xff0c;集成到常规测试三、支持其他的几种四、总结 一、简介 Puppeteer是一个提供自动化控制Chrome或Chromium浏览器的Node…

MyBatis(中)

目录 1、动态sql&#xff1a; 1、if标签&#xff1a; 2、where标签&#xff1a; 3、 trim标签&#xff1a; 4、set标签&#xff1a; 5、choose when otherwise&#xff1a; 6、模糊查询的写法&#xff1a; 7、foreach标签&#xff1a; &#xff08;1&#xff09;批量删除…

施耐德Unity通过Modbus控制变频器

硬件设备 PLC: Unity Premium (CPU:TSX P57154) 通讯卡: TSX SCP 114 连接电缆: TSX SCP CM 4030 VSD: ATV 58 硬件连接 Unity Premium (CPU: TSX P57154)本身不带Modbus接口&#xff0c;因此&#xff0c;采用TSX SCP 114扩展一个Modbus接口。TSX SCP 114是一个RS-485接…

【已解决】No Python at ‘D:\Python\python.exe‘

起因&#xff0c;我把我的python解释器&#xff0c;重新移了个位置&#xff0c;导致我在Pycharm中的爬虫项目启动&#xff0c;结果出现这个问题。 然后&#xff0c;从网上查到了这篇博客: 【已解决】No Python at ‘D:\Python\python.exe‘-CSDN博客 但是&#xff0c;按照上述…

8.Covector Transformation Rules

上一节已知&#xff0c;任意的协向量都可以写成对偶基向量的线性组合&#xff0c;以及如何通过计算基向量穿过的协向量线来获得协向量分量&#xff0c;且看到 协向量分量 以 与向量分量 相反的方式进行变换。 现要在数学上确认协向量变换规则是什么。 第一件事&#xff1a;…

前端小案例 | 一个带切换的登录注册界面(静态)

文章目录 &#x1f4da;HTML&#x1f4da;CSS&#x1f4da;JS &#x1f4da;HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sc…

紫光同创FPGA实现UDP协议栈网络视频传输,基于YT8511和RTL8211,提供4套PDS工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的以太网方案紫光同创FPGA精简版UDP方案紫光同创FPGA带ping功能UDP方案 3、设计思路框架OV7725摄像头配置及采集OV5640摄像头配置及采集UDP发送控制视频数据组包数据缓冲FIFOUDP协议栈详解RGMII转GMII动态ARPUDP协议IP地址、端口…