在本章中,你将学会运用MatchedGeometryEffect构建一个导航菜单。

在构建SwiftUI应用过程中,咱们常常会运用TabView构建底部菜单,但更多的时分会因为咱们定制化的需求,需求咱们自己绘制底部菜单。

那么本章中,咱们就来试试构建一个底部导航菜单

项目建立

首要,创立一个新的SwiftUI项目,命名为NavigationMenu

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

款式建立

咱们先构建导航菜单的款式部分,咱们先声明一个数组存储咱们需求用到的底部菜单

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)
}
}

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

上述代码中,咱们运用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
}
}

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

上述代码中,咱们经过当时菜单索引index来判别当时菜单选中状况。

当菜单处于选中时,咱们运用原来蓝色布景 白色字体的款式,当菜单处于未选中时,咱们设置为灰色布景 黑色文字,且点击时切换当时index为选中索引。

这样,咱们就完结了导航菜单的选中切换交互。

导航切换动画

完结根底的导航菜单切换后,咱们加一点“特效”,咱们运用@Namespace声明一个动画变量Transition。示例:

@Namespace private var Transition

然后咱们给导航菜单选中的状况款式加一个过渡动画。示例:

.matchedGeometryEffect(id: "menuItem", in: Transition)

上述代码中,idnamespace能够标识哪些视图属于转场动画 Transition。一起还需求在点击进行状况切换的时分启用动画作用。示例:

.onTapGesture {
 withAnimation(.easeOut) {
  selectedItem = index
 }
}

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

最终,咱们在调整下位置,看看最终作用。

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

导航菜单进阶

文字导航现已完结了,在常用的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视图外,预览看下作用:

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

这样,咱们就完结了一个图标加文字的底部导航菜单。

导航菜单布景

当咱们给整个视图填充一个布景色彩时,咱们发现出了点问题。因为咱们底部导航菜单并没有布景色彩,导致在白色布景下看起来还不错,但其他布景色彩下就会和布景色彩融为一体,这不是咱们想要的结果。

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

咱们测验加一个圆角白色的布景作为底部菜单的显现区域看看。示例:

.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))

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

上述代码中,咱们增加了底部菜单左右上下的边距,然后将布景色彩设置为白色圆角设置为60

为了能在白色视图布景也能看到底部导航的显现区域,咱们运用overlay修饰符给底部菜单加了一个边框线。咱们切换回白色视图布景看看作用:

SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单

看起来不错的样子!

当然底部导航菜单还有其他款式,能够测验将整个底部菜单区域都变成白色,这是最常用的底部菜单布景的设置办法,也能够在白色的根底上参加含糊通明作用,相似一些社交软件的常用做法,等等。

本章代码

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))
}
}
}
}

祝贺你,完结了本章的一切内容!

快来着手试试吧!

如果本专栏对你有协助,无妨点赞、评论、重视~

我正在参加技术社区创作者签约方案招募活动,点击链接报名投稿。