项目搭建
首先,创建一个新的SwiftUI
项目,命名为NavigationMenu
。
样式搭建
我们先构建导航菜单的样式部分,我们先声明一个数组存储我们需要用到的底部菜单。
let menuItems = ["首页", "发现", "关注", "我的"] 复制代码
然后,我们使用ForEach
循环和Text
文字创建导航菜单样式,示例:
HStack { ForEach(menuItems.indices,id: \.self) { index in Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .background(Capsule().foregroundColor(Color.blue)) .foregroundColor(.white) } } 复制代码
上述代码中,我们使用ForEach
循环和Text
文字创建了4个导航菜单,并设置了背景填充色为蓝色,文字设置为白色。
菜单切换交互
效果不错,然后给导航菜单加一个状态,当我们点击菜单时,切换选中。示例:
@State var selectedItem = 0 复制代码
然后根据选中状态的不同,变换导航菜单的颜色,达到点击切换的效果。示例:
if index == selectedItem { Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .background(Capsule().foregroundColor(Color.blue)) .foregroundColor(.white) } else { Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .background(Capsule().foregroundColor(Color(.systemGray6))) .foregroundColor(.black) .onTapGesture { selectedItem = index } } 复制代码
上述代码中,我们通过当前菜单索引index
来判断当前菜单选中状态。
当菜单处于选中时,我们使用原来蓝色背景白色字体的样式,当菜单处于未选中时,我们设置为灰色背景黑色文字,且点击时切换当前index
为选中索引。
这样,我们就实现了导航菜单的选中切换交互。
导航切换动画
完成基础的导航菜单切换后,我们加一点“特效”,我们使用@Namespace
声明一个动画变量Transition
。示例:
@Namespace private var Transition 复制代码
然后我们给导航菜单选中的状态样式加一个过渡动画。示例:
.matchedGeometryEffect(id: "menuItem", in: Transition) 复制代码
上述代码中,id
和namespace
可以标识哪些视图属于转场动画 Transition
。同时还需要在点击进行状态切换的时候启用动画效果。示例:
.onTapGesture { withAnimation(.easeOut) { selectedItem = index } } 复制代码
最后,我们在调整下位置,看看最终效果。
导航菜单进阶
文字导航已经完成了,在常用的App中会使用图片+文字的形式构建底部导航菜单,我们这边也试试。
首先先定义好使用的图标数组,示例:
let menuImages = ["house", "paperplane", "heart", "person.2"] 复制代码
由于文字和图标是纵向排列,我们使用VStack
构建页面,示例:
选中部分
VStack { Image(systemName: menuImages[index]+".fill") .foregroundColor(Color.blue) .font(.system(size: 24)) Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .foregroundColor(.blue) } 复制代码
上述代码中,我们在VStack
纵向视图中使用Image
图标和Text
文字构架了选中的样式,图标部分使用图标+fill
修饰符变为填充样式,同时图标颜色设置为蓝色。文字部分,我们把文字颜色修改为蓝色作为选中颜色。
非选中部分
VStack { Image(systemName: menuImages[index]) .foregroundColor(Color(.systemGray4)) .font(.system(size: 24)) Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .foregroundColor(Color(.systemGray4)) } 复制代码
对于非选中部分,我们也同样处理,只是把颜色变成了灰色。
最后将.matchedGeometryEffect
修饰符和.onTapGesture
调整为VStack
视图外,预览看下效果:
这样,我们就完成了一个图标加文字的底部导航菜单。
导航菜单背景
当我们给整个视图填充一个背景颜色时,我们发现出了点问题。由于我们底部导航菜单并没有背景颜色,导致在白色背景下看起来还不错,但其他背景颜色下就会和背景颜色融为一体,这不是我们想要的结果。
我们尝试加一个圆角白色的背景作为底部菜单的显示区域看看。示例:
.padding(.horizontal,30) .padding(.top,10) .padding(.bottom,10) .background(Color.white) .cornerRadius(60) .overlay(RoundedRectangle(cornerRadius: 60) .stroke(Color(red: 220/255, green: 223/255, blue: 230/255), lineWidth: 1)) 复制代码
上述代码中,我们增加了底部菜单左右上下的边距,然后将背景颜色设置为白色,圆角设置为60。
为了能在白色视图背景也能看到底部导航的显示区域,我们使用overlay
修饰符给底部菜单加了一个边框线。我们切换回白色视图背景看看效果:
看起来不错的样子!
当然底部导航菜单还有其他样式,可以尝试将整个底部菜单区域都变成白色,这是最常用的底部菜单背景的设置方法,也可以在白色的基础上加入模糊透明效果,类似一些社交软件的常用做法,等等。
本章代码
import SwiftUI struct ContentView: View { let menuItems = ["首页", "发现", "关注", "我的"] let menuImages = ["house", "paperplane", "heart", "person.2"] @State var selectedItem = 0 @Namespace private var Transition var body: some View { ZStack { Color.white .ignoresSafeArea() VStack { Spacer() HStack { ForEach(menuItems.indices, id: \.self) { index in if index == selectedItem { VStack { Image(systemName: menuImages[index]+".fill") .foregroundColor(Color.blue) .font(.system(size: 24)) Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .foregroundColor(.blue) }.matchedGeometryEffect(id: "menuItem", in: Transition) } else { VStack { Image(systemName: menuImages[index]) .foregroundColor(Color(.systemGray4)) .font(.system(size: 24)) Text(menuItems[index]) .padding(.horizontal) .padding(.vertical, 4) .foregroundColor(Color(.systemGray4)) } .onTapGesture { withAnimation(.easeOut) { selectedItem = index } } } } } .padding(.horizontal,30) .padding(.top,10) .padding(.bottom,10) .background(Color.white) .cornerRadius(60) .overlay(RoundedRectangle(cornerRadius: 60) .stroke(Color(red: 220/255, green: 223/255, blue: 230/255), lineWidth: 1)) } } } } 复制代码
恭喜你,完成了本章的所有内容!
快来动手试试吧!