iOS开发 SwiftUI 8:NavigationView 导航

注意NavigationView已经废弃,替代品是NavigationStack。

目录

[NavigationView 标题和按钮](#NavigationView 标题和按钮)

标题显示模式

导航栏按钮

注意,实时预览可能没有反应

实现页面导航

NavigationLink

完整导航页面示例

导航嵌套

嵌套视图的标题

技巧

用Spacer使Stack最大化

View用作参数并给View传递参数

自定义返回按钮


导航视图指的是顶部的一两行区域,称为顶部导航或导航栏。顶部一般会包括页面标题和导航按钮,下面的示例是基本的用法:

Swift 复制代码
struct SwiftUIViewNavigation: View {
    var body: some View {
        NavigationView(){
            VStack{
                Text("Hello, World!")
                Spacer()
                Text("Hello, World!")
            }
            .navigationBarTitle("123",displayMode: .inline)//标记1
            .navigationBarItems(trailing: titleLineView)//标记2
            .navigationBarItems(leading: titleLineView, trailing: titleLineView)//标记3
        }
    }
    
    private var titleLineView: some View{
        Button(action:{}){
            VStack{
                Image(systemName: "square.and.arrow.up")
                Image(systemName: "square.and.arrow.up")
            }
        }
    }
}

代码中关键三行代码做了标注,分别设置了标题、显示方式、前后按钮。注意这些修饰是在NavigationView的内部进行的,如果有多个navigationBarTitle()设置了不同的标题,实测内层的生效,而示例中连用两个navigationBarItems的效果是叠加。

上面代码的效果:

标题显示模式

上面的代码设置显示模式为inline,标题和按钮会显示在同一行,如果改为automatic或large,则效果为:

可以注意到分成了两行,标题独占一行。

导航栏按钮

导航栏前后按钮都是视图,上面的代码用了一个稍微复杂的视图演示了视图超出导航栏大小时的效果。

上面的代码用VStack组合了两个图标,超出了导航栏高度,所以显示不全。正常情况应该用HStack来组合多个按钮,下面的代码增加了绑定来显示哪个按钮被点击:

Swift 复制代码
struct SwiftUIViewNavigation: View {
    @State var info : String = "info"
    var body: some View {
        NavigationView(){
            VStack{
                Text(info)
                Spacer()
                Text("Hello, World!")
            }
            .navigationBarTitle("123",displayMode: .automatic)
            .navigationBarItems(trailing: titleLineView)
            .navigationBarItems(leading: titleLineView, trailing: titleLineView)
        }
    }
    
    private var titleLineView: some View{
        HStack{
            Button(action:{info="a"}){
                Image(systemName: "square.and.arrow.up.fill")
            }
            Button(action:{info="b"}){
                Image(systemName: "square.and.arrow.up")
            }
        }
    }
}

点击效果:

注意,实时预览可能没有反应

今天修改代码的时候实时预览经常出问题,要多些改动实时预览才会生效(敲一些空格回车)。

实现页面导航

NavigationLink可以实现跳转,需要两个参数:标签和跳转目标视图(当然,标签也是个视图):

Swift 复制代码
struct NavigationLink<Label, Destination> where Label : View, Destination : View

用法很简单,标签可以用字符串,视图可以是已经做好的任何页面。

完整导航页面示例

下面是一个完整的导航页面的例子,里面包含了一些布局和模块化代码的技巧。

Swift 复制代码
import SwiftUI

struct SwiftUIViewNavigation: View {
    @State var info: String = "info"
    var body: some View {
        NavigationView {
            HStack {
                VStack(alignment: .leading, spacing: 20) {
                    VStack(alignment: .leading, spacing: 20) {
                        MenuItemView(imageName: "person", itemName: "账号设置"){ InputView() }
                        MenuItemView(imageName: "person", itemName: "账号设置"){ InputView() }
                        MenuItemView(imageName: "person", itemName: "账号设置"){ InputView() }
                        Spacer()

                    }
                    .navigationBarTitle("导航", displayMode: .inline)
                    .navigationBarItems(
                        leading: titleLineViewLeft,
                        trailing: titleLineViewWright
                    )

                    HStack {
                        Text(info).padding(10)
                        Spacer()
                    }.background(.cyan)

                }
                .padding(.all, 20)
                Spacer()
            }.border(.red)
        }
    }
    struct MenuItemView<destinationView: View>: View {
        var imageName: String
        var itemName: String
        @ViewBuilder public var destinationview: () -> destinationView
        var body: some View {
            NavigationLink(destination: destinationview) {
                HStack(spacing: 20) {
                    Image(systemName: imageName)
                    Text(itemName)
                }
            }
        }
    }
    private var titleLineViewLeft: some View {
        HStack {
            Button(action: { info = "返回" }) {
                Image(systemName: "chevron.backward")
            }
        }
    }
    private var titleLineViewWright: some View {
        HStack {
            Button(action: { info = "搜索" }) {
                Image(systemName: "magnifyingglass")
            }
            Button(action: { info = "分享" }) {
                Image(systemName: "square.and.arrow.up")
            }
        }
    }

}

效果:

三个导航链接是一样的,点击之后是:

InputView 视图代码是这样的:

Swift 复制代码
import SwiftUI

struct InputView: View {
	@State var text="a\r\nb"
    var body: some View {
		VStack
		{
			Text("\(text)")
				.padding()
				.frame(width: 300,height: 100)
				.border(.black,width: 5)
				.multilineTextAlignment(.leading)
			
			TextEditor(text:$text)
				.padding()
				.frame(width: 300,height: 100)
				.border(.black,width: 5)
			
			TextField("Hello, World!",text:$text)
				.padding()
				.frame(width: 300,height: 100)
				.border(.black,width: 5)
		}
    }
}

#Preview {
    InputView()
}

独立的预览效果是这样的:

通过导航进入和独立页面的区别是上面有个返回按钮(和导航页我们自己做的图标看起来是一样的,但是这个是系统提供的),点击返回按钮可以回到导航页面。

导航嵌套

前面使用的InputView是个简单页面,如果换成另一个导航页面会怎么样?将导航目标替换为自身:

Swift 复制代码
MenuItemView(imageName: "person", itemName: "账号设置"){ SwiftUIViewNavigation() }

效果:

多点几次:

非常明显,导航效果是嵌套的,目标页面完整嵌套在子视图区域。

嵌套视图的标题

嵌入当然也应该有自己的标题。之前我们已经知道多次设置navigationBarTitle内层的生效,嵌套的就处于内层,所以前台页面的设置会替代导航页面的设置。

给InputView视图加上navigationBarTitle设置:

Swift 复制代码
struct InputView: View {
	@State var text="a\r\nb"
    var body: some View {
		VStack
		{
。。。。。。
		}
        .navigationBarTitle("InputView", displayMode: .inline)
    }
}

注意这里设置了displayMode,这是不会改变导航页的显示布局的。也就是说,如果设置成large或自动,而导航页面是inline,由于嵌套视图的位置已经确定,并没有留出large的显示位置,嵌套视图的标题会被遮盖,从而无法看到。

正常的显示效果:

技巧

用Spacer使Stack最大化

HStack和VStack默认不是尽可能大的,内部子视图对齐方式只能在有效范围内对齐,而不是我们期待的整个屏幕。当我们想实现基于屏幕的靠左或靠右时,需要先把Stack最大化。

frame是一个办法,但是非常不理想,通过嵌套一个H/VStack加上Spacer是最直白的。

View用作参数并给View传递参数

View大量用作参数,包括很多习惯上是个字符串的其实也是View参数(比如导航链接的标签参数)。

我们前面已经用过不带参数的子视图,非常简单,直接构造一个View即可:

Swift 复制代码
    private var titleLineViewLeft: some View {
        HStack {
            Button(action: { info = "返回" }) {
                Image(systemName: "chevron.backward")
            }
        }
    }

但是需要参数化的视图就比较麻烦,因为内部实现对视图渲染有要求。代码可以照下面的葫芦来画瓢:

Swift 复制代码
    struct MenuItemView<destinationView: View>: View {
        var imageName: String
        var itemName: String
        @ViewBuilder public var destinationview: () -> destinationView
        var body: some View {
            NavigationLink(destination: destinationview) {
                HStack(spacing: 20) {
                    Image(systemName: imageName)
                    Text(itemName)
                }
            }
        }
    }

调用:

MenuItemView(imageName: "person", itemName: "账号设置"){ InputView() }

视图参数如果有多个,排在第一个的是默认的,后面的都需要名字加冒号再加大括号。

自定义返回按钮

打开的新视图嵌套在导航框架里,具有一个标准的返回按钮。我们可以隐藏这个按钮并替换成我们自己的视图。

修改InputView:

Swift 复制代码
struct InputView: View {
    @Environment(\.presentationMode) var presentationMode //用来实现返回的环境值
    @State var text="a\r\nb"
    var body: some View {
        VStack
        {
            。。。。。。
        }
        .navigationBarTitle("InputView", displayMode: .inline)
        .navigationBarBackButtonHidden() //隐藏默认返回按钮
        .navigationBarItems(leading: titleLineViewLeft) //添加自定义返回按钮
    }
//自定义返回按钮
    private var titleLineViewLeft: some View {
        HStack {
            Button(action: { self.presentationMode.wrappedValue.dismiss()}) {
                Image(systemName: "chevron.backward")
            }
        }
    }
}

因为我们用的图标和系统是一样的,看不出区别,所以可以屏蔽掉.navigationBarBackButtonHidden()看看效果:

出现了两个一模一样的返回按钮,说明代码生效了。此时我们还可以试一下侧滑返回,是有效的。

我们再把.navigationBarBackButtonHidden()这句放出来,默认返回按钮就没有了,但是侧滑返回也没有了。当然有办法用代码实现侧滑返回,不过,如果不是游戏应用,有点吃饱了撑的。

相关推荐
游戏开发爱好者86 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
黑码哥7 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
2501_915106328 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
2501_915106328 小时前
使用 Sniffmaster TCP 抓包和 Wireshark 网络分析
网络协议·tcp/ip·ios·小程序·uni-app·wireshark·iphone
熊猫钓鱼>_>9 小时前
移动端开发技术选型报告:三足鼎立时代的开发者指南(2026年2月)
android·人工智能·ios·app·鸿蒙·cpu·移动端
徐同保1 天前
通过ip访问nginx的服务时,被第一个server重定向了,通过设置default_server解决这个问题
ios·iphone
2501_915918411 天前
在 iOS 环境下查看 App 详细信息与文件目录
android·ios·小程序·https·uni-app·iphone·webview
2501_916007471 天前
没有 Mac 用户如何上架 App Store,IPA生成、证书与描述文件管理、跨平台上传
android·macos·ios·小程序·uni-app·iphone·webview
夏幻灵2 天前
HTTPS全面解析:原理、加密机制与证书体
ios·iphone
TheNextByte12 天前
如何在iPhone上恢复已删除的笔记的综合指南
笔记·ios·iphone