SwiftUI极简教程13:NavigationView导航栏使用

7,081 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

前言:电脑无缘无故死机2次,可能是因为一边做UI,一边写代码,一边写文章吧,可辛苦电脑了。不过也幸好文章是实时保存的,每次打开原来写的还在,可吓死我了。坚持码字,坚持更新,今天是日更第13天啦,加油!

今日职言:为某一个人而写作。

在本章中,你将学会如何使用NavigationView构建基础的导航栏,以及基于导航栏的页面跳转。

如果你恰好用过UIkit,应该尝试过用storyboard或者UINavigationController来构建页面导航。

而在SwiftUI中,我们使用NavigationView,而它的使用方法和上一章List基本一样。

本章节将分成3个部分讲解。

1、构建一个简单的导航栏;

2、基于导航栏的页面跳转;

3、导航栏自定义返回;

第一部分:构建一个简单的导航栏

我们先尝试在原来的List上增加NavigationView看看导航栏的效果。

UI稿如下:

215.png

首先,我们先创建一个新项目,命名为SwiftUINavigationView。

216.png

然后,我们把上一章的代码拿过来作为示例讲解。

代码如下:

import SwiftUI
struct ContentView: View {

    // 定义数组,存放数据
    var Messages = [
        Message(name: "这是微信", image: "weixin"),
        Message(name: "这是微博", image: "weibo"),
        Message(name: "这是QQ", image: "qq"),
        Message(name: "这是电话", image: "phone"),
        Message(name: "这是邮箱", image: "mail")
    ]

    var body: some View {

        // 列表
        List(Messages) { Message in
            HStack {
                Image(Message.image)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(5)
            Text(Message.name)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct Message: Identifiable {
    var id = UUID()
    var name: String
    var image: String
}

217.png

NavigationView的构建和List方法一样,将我们需要的内容包裹在NavigationView里面就行了。

NavigationView{  }

这里注意的是,需要把整个List包裹起来。

包裹完成后,我们可以看到整个List顶部留出了导航栏的位置。

//导航栏
NavigationView{

    // 列表
    List(Messages) { Message in
        HStack {
            Image(Message.image)
                .resizable()
                .frame(width: 40, height: 40)
                .cornerRadius(5)
            Text(Message.name)
            }
        }
    }

218.png

我们尝试加个标题,这里我们用的修饰符是:

.navigationBarTitle("我是标题")

修饰符需要加在内容最外层,而不是NavigationView外层。

这样,我们就得到了一个拥有大标题的列表。

//导航栏
NavigationView {

    // 列表
    List(Messages) { Message in
        HStack {
            Image(Message.image)
                .resizable()
                .frame(width: 40, height: 40)
                .cornerRadius(5)
            Text(Message.name)
        }
    }.navigationBarTitle("我是标题")
}

219.png

是不是挺简单!

SwiftUI将很多事情都简单化了,以至于我们在使用不同的控件的方法基本都差不多。

第二部分:基于导航栏的页面跳转

下面进阶一下。

我们尝试从一个页面跳转到第二个页面,并且还能返回。

UI稿如下:

220.png

我们先完成第二个页面的内容,然后再从第一个页面点击对应的列表进入第二个页面。

我们在ContentView页面最后创建一个新的页面,命名为DetailView。

代码如下:

//详情页面
struct DetailView: View {

    var message: Message

    var body: some View {
        VStack {
            Image(message.image)
                .resizable()
                .frame(width: 80, height: 80)
            Text(message.name)
                .font(.system(.title, design: .rounded))
                .fontWeight(.black)
                
            Spacer()
        }
    }
}

我们在DetailView里面引用Message数组的数据,将Image图片和Text文字换成数组的参数。

这样我们就可以第一个页面点击哪一个列表,然后就能将对应的图片和文字带入到第二个页面,也就是所谓的传参。

221.png

然后回到第一个页面里。

我们需要点击List列表,然后跳转到第二个页面。

这时候我们需要使用一个新的参数,叫做NavigationLink。

NavigationLink(destination: DetailView()) {
    //点击跳转的内容
}

destination表明我们要跳转的页面,这里我们需要跳转的页面是DetailView。

我们需要点击List里面的一行,然后跳转到第二个页面,因此NavigationLink需要放在List里面,而不是包裹住整个List。

如果NavigationLink包裹住List,那么就变成了点击整个List跳转了,这里我们需要的是点击List单独一行对应跳转。

NavigationView {

    // 列表
    List(Messages) { Message in
        NavigationLink(destination: DetailView(message: Message)) {
            HStack {
                Image(Message.image)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(5)
                Text(Message.name)
            }
        }
    }.navigationBarTitle("我是标题")
}

222.png

我们可以看到,跳转的目标是DetailView,并且我们把Message结构体的参数传递给了DetailView页面的message变量。

这样就实现了我们Message结构体里的参数传递给message,也就实现了,我们点击的Message是哪一个,DetailView页面的message对应传递哪一个参数。

我们点击模拟器上面的Preview运行下模拟器,尝试点击List其中一行,就可以体验下点击跳转页面,再点击返回到主页的效果。

223.png

科普一个知识点。

我们在常用的App当中看到的顶部导航栏文字是居中的,这是怎么实现的呢?

也很简单,ios默认的是大标题效果(.automatic),也就是:

.navigationBarTitle("我是标题", displayMode: .automatic)

我们只需要给.navigationBarTitle("我是标题”)里加入参数控制(.inline),就可以实现文字标题居中效果。

.navigationBarTitle("我是标题", displayMode: .inline)

224.png

好了,至此我们就完成了基于导航栏的页面跳转方式啦!

第三部分:导航栏自定义返回

接下来,让我们看看DetailView页面,我们会发现,这个“返回”的操作似乎没那么好看。

225.png

很多时候,我们的“返回”操作可能是一个图标按钮,也可能是“文字”按钮。

我们有没有办法美化下这个返回首页的操作?

让我们分析下。

我们只需要把系统自带的返回操作隐藏,然后再自己“写”一个返回操作就可以了。

这里我们用到了隐藏导航返回按钮的参数:

.navigationBarBackButtonHidden(true)

226.png

下一步,我们自己写一个返回的按钮,在这里,我们需要用到另一个参数:

.navigationBarItems(leading: Button(action : {

    // 点击按钮后的操作

    }){

    //按钮及其样式
    Image(systemName: "chevron.left")
        .foregroundColor(.gray)
})

我们试试用图标按钮作为返回按钮。

这里使用的是系统自带的图片,填充颜色为灰色。

228.png

运行看看,我们可以点击主页列表数据进入详情页。

但是,我们点击返回操作没有反应。

这是因为我们还没有实现返回的方法。

SwiftUI提供了(.presentationMode)内置环境值,我们可以用这个环境值实现返回到前一个视图的操作。

@Environment(\.presentationMode) var mode

然后在返回按钮的点击操作时调用环境值的dismiss()函数,就可以实现返回的操作。

self.mode.wrappedValue.dismiss()

DetailView页面完整代码如下:

// 详情页面
struct DetailView: View {

    //环境值
    @Environment(\.presentationMode) var mode
    var message: Message

    var body: some View {

        VStack {

            Image(message.image)
                .resizable()
                .frame(width: 80, height: 80)

            Text(message.name)
                .font(.system(.title, design: .rounded))
                .fontWeight(.black)

            Spacer()

        }
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: Button(action : {

            // 点击按钮后的操作
            self.mode.wrappedValue.dismiss()

            }){

            //按钮及其样式
            Image(systemName: "chevron.left")
                .foregroundColor(.gray)
        })
    }
}

229.png

运行下代码,我们发现实现自定义按钮的返回操作啦!

好了,今天的知识点就到这里了。

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