SwiftUI完成了伸缩式导航栏!好家伙,还不收藏一波~

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

项目背景

在逛某站的时候,看到一个使用HTMLl+CSS构建一个伸缩式导航栏的案例,觉得蛮有意思。

在客户端开发中,导航栏是必不可少的设计元素之一。但纵观很多国内的App,基本都是最为基础的导航栏,缺乏新意。

在本章中,我们就试试使用SwiftUI构建一个伸缩式导航栏,作为练习。

那么,让我们开始吧。

项目搭建

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

1.png

收缩视图

样式设计方面,由于我们需要构建一个伸缩式的导航栏,那么需要拆解成伸展式收缩式两种样式效果。

首先我们需要声明一个变量来存储它的状态,示例:

@State var isFold: Bool = false
复制代码

然后我们来构建收缩时的样式,示例:

// 收起效果
func foldView() -> some View {
    Image(systemName: "list.bullet")
        .font(.system(size: 24))
        .frame(minWidth: 0, maxWidth: 60, minHeight: 0, maxHeight: 60)
        .foregroundColor(.black)
        .background(.white)
        .cornerRadius(30)
        .onTapGesture {
            withAnimation(.spring()) {
                self.isFold.toggle()
            }
        }
}
复制代码

2.png

上述代码中,我们构建了收缩时的样式视图foldView。除了基本的视图样式外,我们在点击这个按钮视图时,切换isFold的状态。

展开视图

然后时展开视图,展开视图部分有两部分组成,一部分是“关闭”按钮,另一块则是标准的导航栏,我们来构建样式。

首先需要声明一个数组来表明我们导航栏内容,示例:

let menuItems = ["首页", "沸点", "课程", "我的"]
复制代码

然后再声明一个变量来表示当前选中的导航栏的栏目,示例:

@State var selectedItem = 0
复制代码

最后,我们创建一个新的视图来完成展开视图的样式部分,示例:

// 展开效果
func unfoldView() -> some View {
    HStack {
        Image(systemName: "xmark")
            .font(.system(size: 24))
            .foregroundColor(.pink)
            .onTapGesture {
                withAnimation(.spring()) {        
                    self.isFold.toggle()
                }
            }

        ForEach(menuItems.indices, id: \.self) { index in
            if index == selectedItem {
                Text(menuItems[index])
                    .font(.system(size: 17))
                    .padding(.horizontal, 15)
                    .foregroundColor(.pink)
            } else {
                Text(menuItems[index])
                    .font(.system(size: 17))
                    .padding(.horizontal, 15)
                    .foregroundColor(.black)
                    .onTapGesture {
                        selectedItem = index
                    }
            }
        }
    }
    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 60)
    .background(.white)
    .cornerRadius(30)
    .padding(.horizontal)
}
复制代码

3.png

上述代码中,我们创建了一个展开时的视图unfoldView

然后使用HStack横向视图布局,将一个按钮和导航栏文字内容并排展示,当我们点击“关闭”按钮的时候,切换isFold的状态,以及我们还根据当前点击的index的位置,更新selectedItem选中的栏目,当选中时,我们给选中的栏目设置一个粉色的文字颜色用于区分。

主页视图

完成两个视图后,我们返回ContentView视图,我们根据isFold设置视图,示例:

var body: some View {
    ZStack {
        Color(.systemGray6)

        if isFold {
            unfoldView()
        } else {
            foldView()
        }
    }.edgesIgnoringSafeArea(.all)
}
复制代码

4.gif

效果还不错,只是缺少了过渡动画,整个交互看起来略显生硬。

过渡动画

我们先声明一个变量来存储交换位置的状态,示例:

@Namespace private var Transition
复制代码

然后使用matchedGeometryEffect修饰符进行位置过渡切换,matchedGeometryEffect修饰符需要修饰两个按钮,示例:

//收起时按钮
Image(systemName: "list.bullet”)
    .matchedGeometryEffect(id: "fold", in: Transition)    

//展开时按钮
Image(systemName: "xmark”)
    .matchedGeometryEffect(id: "fold", in: Transition)
复制代码

这里如果我们要看到最终的效果,我们需要运行模拟器,在真机环境下才能看到最终的交互效果。

项目预览

5.gif

恭喜你,完成了本章的全部内容!

快来动手试试吧。

如果本专栏对你有帮助,不妨点赞、评论、关注~

猜你喜欢

转载自juejin.im/post/7135063367349174303