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

简介: 在本章中,你将学会使用MatchedGeometryEffect构建一个导航菜单。在构建SwiftUI应用过程中,我们常常会使用TabView构建底部菜单,但更多的时候会由于我们定制化的需求,需要我们自己绘制底部菜单。那么本章中,我们就来试试构建一个底部导航菜单。

image.png

项目搭建

首先,创建一个新的SwiftUI项目,命名为NavigationMenu

image.png

样式搭建

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

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

image.png

上述代码中,我们使用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
        }
}
复制代码

image.png

上述代码中,我们通过当前菜单索引index来判断当前菜单选中状态。

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

这样,我们就实现了导航菜单的选中切换交互。

导航切换动画

完成基础的导航菜单切换后,我们加一点“特效”,我们使用@Namespace声明一个动画变量Transition。示例:

@Namespace private var Transition
复制代码

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

.matchedGeometryEffect(id: "menuItem", in: Transition)
复制代码

上述代码中,idnamespace可以标识哪些视图属于转场动画 Transition。同时还需要在点击进行状态切换的时候启用动画效果。示例:

.onTapGesture {
    withAnimation(.easeOut) {
        selectedItem = index
    }
}
复制代码

image.png

最后,我们在调整下位置,看看最终效果。

image.png

导航菜单进阶

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

image.png

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

导航菜单背景

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

image.png

我们尝试加一个圆角白色的背景作为底部菜单的显示区域看看。示例:

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

image.png

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

为了能在白色视图背景也能看到底部导航的显示区域,我们使用overlay修饰符给底部菜单加了一个边框线。我们切换回白色视图背景看看效果:

image.png

看起来不错的样子!

当然底部导航菜单还有其他样式,可以尝试将整个底部菜单区域都变成白色,这是最常用的底部菜单背景的设置方法,也可以在白色的基础上加入模糊透明效果,类似一些社交软件的常用做法,等等。

本章代码

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

恭喜你,完成了本章的所有内容!

快来动手试试吧!


相关文章
|
6月前
SAP UI5 控件 ObjectStatus 的使用方法介绍试读版
SAP UI5 控件 ObjectStatus 的使用方法介绍试读版
51 0
SAP UI5 控件 ObjectStatus 的使用方法介绍试读版
|
7月前
|
XML 数据格式 开发者
SAP UI5 初学者教程之十五 - 对话框和 Fragments 的使用方式试读版
SAP UI5 初学者教程之十五 - 对话框和 Fragments 的使用方式试读版
33 0
|
7月前
|
Web App开发 前端开发 JavaScript
SAP UI5 应用 SimpleForm 控件 ResponsiveGridLayout 布局的工作原理深入剖析试读版
SAP UI5 应用 SimpleForm 控件 ResponsiveGridLayout 布局的工作原理深入剖析试读版
48 0
|
7月前
|
数据可视化
使用 SAP UI5 3D Viewer 控件显示 3D 模型效果试读版
使用 SAP UI5 3D Viewer 控件显示 3D 模型效果试读版
33 0
|
前端开发 API 开发者
使用 SAP UI5 Smart Chart 控件轻松绘制十数种不同类型的专业图表试读版
使用 SAP UI5 Smart Chart 控件轻松绘制十数种不同类型的专业图表试读版
|
存储 索引
SwiftUI极简教程19:SwipeCard卡片滑动效果的使用(下)
SwiftUI极简教程19:SwipeCard卡片滑动效果的使用(下)
529 0
SwiftUI极简教程19:SwipeCard卡片滑动效果的使用(下)
|
程序员 索引
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
850 0
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
|
图形学
Unity 编辑器开发实战【Custom Editor】- 为UI视图制作动画编辑器
Unity 编辑器开发实战【Custom Editor】- 为UI视图制作动画编辑器
199 1
Unity 编辑器开发实战【Custom Editor】- 为UI视图制作动画编辑器
|
API iOS开发
SwiftUI 中的自定义导航
默认情况下,SwiftUI提供的各种导航API在很大程度上是以用户直接输入为中心的——也就是说,导航是在系统响应例如按钮的点击和标签切换等事件时由系统本身处理的。
227 0
SwiftUI 中的自定义导航
|
索引
SwiftUI极简教程25:构建一个Banner图片轮播(中)
SwiftUI极简教程25:构建一个Banner图片轮播(中)
327 0
SwiftUI极简教程25:构建一个Banner图片轮播(中)