SwiftUI 4.0:两种方式实现子视图导航功能

在这里插入图片描述

0. 概览

从 SwiftUI 4.0 开始,觉悟了的苹果毅然抛弃了已“药石无效”的 NavigationView,改为使用全新的 NavigationStack 视图。

诚然,NavigationStack 从先进性来说比 NavigationView 有不小的提升,若要如数家珍得单开洋洋洒洒的一篇来介绍。


关于 SwiftUI 中旧 NavigationView 视图种种人神共愤的“诟病”和弊端,请移步我的其它专题博文观赏:

  • SwiftUI进入多重嵌套视图后如何一键退回到根视图
  • iOS 16.2 在 SwiftUI 子视图中无法关闭弹出的(sheet)导航视图(NavigationView)之解决
  • SwiftUI导航至子视图后状态改变导致导航栈提前弹出的原因及解决
  • SwiftUI实现不同TabView标签页中任意导航层级视图之间自动相互跳转那些事儿
  • SwiftUI中NavigationLink多层嵌套导航无法返回上一层的原因及解决

不过,这些都不是本篇的主旨。在本篇中我们将尝试一起另辟蹊径来完成两种截然不同的导航机制。

在本篇博文,您将学到如下内容:

  • 0. 概览
  • 1. NavigationStack
  • 2. NavigationSplitView 导航之“假象”
  • 3. 洞若观火:在 iPad 上的比较
  • 4. 总结

无需等待,Let’s go!!!😉


1. NavigationStack

从 SwiftUI 4.0 开始, 引入的新 NavigationStack 导航器终于不再以分散杂乱的数据作为导航触发媒介,而是将有序的数据集合作为导航跳转的核心来对待!(所以,一步跳回根视图成了雕虫小技)

其中,NavigationStack 导航器重要的组成部分 NavigationLink 除了为了兼容性暂时保留的传统跳转方式以外,主打以状态本身的值作为导航的基石。这样,对于不同类型状态触发的跳转,我们可以干净而从容的分别处理:

下面举一例。

首先,定义简单的数据结构,Alliance 中包含若干 Hero:

@Observable
final class Hero {var name: Stringvar power: Intinit(name: String, power: Int) {self.name = nameself.power = power}
}extension Hero: Identifiable {var id: String {name}
}extension Hero: Hashable {static func == (lhs: Hero, rhs: Hero) -> Bool {lhs.name == rhs.name && lhs.power == rhs.power}func hash(into hasher: inout Hasher) {hasher.combine(name)hasher.combine(power)}
}@Observable
final class Alliance: Hashable {static func == (lhs: Alliance, rhs: Alliance) -> Bool {lhs.title == rhs.title}func hash(into hasher: inout Hasher) {hasher.combine(title)}var title: Stringvar createAt: Date?var heros: [Hero]init(title: String, heros: [Hero]) {self.title = titleself.createAt = Date.nowself.heros = heros}
}

接下来是与之对应的子视图,注意驱动 NavigationLink 导航的是 Hero 本身,而不是什么“莫名奇妙”的条件变量:

struct HeroDetailView: View {let hero: Herovar body: some View {VStack {Text("力量: \(hero.power)").font(.largeTitle)}.navigationTitle(hero.name)}
}struct HeroListView: View {@Environment(Alliance.self) var modelvar body: some View {        VStack {List(model.heros) { hero inNavigationLink(value: hero) {HStack {Text(hero.name).font(.headline)Spacer()Text("\(hero.power)").font(.subheadline).foregroundStyle(.gray)}}}}}
}

接着是主视图:

struct ContentView: View {@State var model = Alliance(title: "地球超级英雄", heros: [.init(name: "大熊猫侯佩", power: 5),.init(name: "孙悟空", power: 1000),.init(name: "哪吒", power: 511)])var body: some View {NavigationStack {Form {NavigationLink("查看所有英雄", value: model)}.navigationDestination(for: Alliance.self) { model inHeroListView().environment(model)}.navigationDestination(for: Hero.self) { hero inHeroDetailView(hero: hero)}.navigationTitle(model.title)}}
}

从上面源代码中,我们可以看到几处有趣的地方:

  1. 子视图 HeroListView 和主视图 ContentView 都包含了 NavigationLink,但它们驱动状态的类型不一样(分别是 Hero 和 Model),这样不同的驱动源被清晰的区分开了;
  2. 放置 NavigationLink 和实际发生导航跳转的目标位置是分开的(通过 navigationDestination() 修改器),后者被放在了一起便于集中管理;

正是这些新的导航特性确保了导航逻辑代码清楚且集中,为日后自己或其它秃头码农来维护打下夯实基础:

在这里插入图片描述

以上就是第一种导航方法,即利用 NavigationStack + navigationDestination() 修改器方法来合作完成跳转功能。

2. NavigationSplitView 导航之“假象”

可能有的小伙伴们没太在意,SwiftUI 4.0 除了 NavigationStack 以外还新加入了另一个鲜为人知的导航器 NavigationSplitView!使用它,我们可以抛弃 navigationDestination() 去实现完全相同的导航功能。

我们对之前代码略作修改,看看能促成什么新奇的“玩法”:

struct HeroListView: View {@Environment(Alliance.self) var model@Binding var selection: Hero?var body: some View {        VStack {List(model.heros, selection: $selection) { hero inNavigationLink(value: hero) {HStack {Text(hero.name).font(.headline)Spacer()Text("\(hero.power)").font(.subheadline).foregroundStyle(.gray)}}}}}
}struct ContentView: View {@State var model = Alliance(title: "地球超级英雄", heros: [.init(name: "大熊猫侯佩", power: 5),.init(name: "孙悟空", power: 1000),.init(name: "哪吒", power: 511)])@State private var selection: Hero?var body: some View {NavigationSplitView(sidebar: {HeroListView(selection: $selection).environment(model).navigationTitle("新导航方式")}, detail: {if let selection {HeroDetailView(hero: selection)} else {ContentUnavailableView("No Hero", systemImage: "person.badge.key.fill", description: Text("还未选中任何英雄!"))}})}
}

可以看到,修改后的代码与之前有几处不同:

  1. 使用 NavigationSplitView 而不是 NavigationStack;
  2. 没有使用任何 navigationDestination() 修改器方法;
  3. 向 List 构造器传入了 selection 参数,以判断用户选择了哪个 Hero;
  4. 根据 selection 的值驱动 NavigationSplitView 构造器 detail 闭包完成跳转功能;

简单来说:当用户选中任意 Hero 后,通过设置 NavigationSplitView 构造器 detail 闭包中的视图,我们完成了导航机制。

代码执行结果和之前几乎完全相同,这么神奇!?

可惜,你们看到的全是“假象”!!!

3. 洞若观火:在 iPad 上的比较

其实,设置 NavigationSplitView 构造器 detail 闭包的内容原本并不会造成导航跳转,它的原意是在大屏设备上更方便的利用大尺寸屏幕来浏览内容。

编译上面 NavigationSplitView 的实现,在 iPad 上运行看看效果:

在这里插入图片描述

看到了吗?第二种导航机制在大屏设备上原本并不是真正用来导航跳转的,只是在 iPhone 等小屏设备上它的行为退化成了导航!

而第一种导航实现是彻头彻尾、如假包换的“真”导航:

在这里插入图片描述

到底哪种方法更好一些?小伙伴们自有“仁者见仁智者见智”的看法,欢迎大家随后的讨论。

至此,我们完成了文章开头的目标,棒棒哒!!!💯

4. 总结

在本篇博文中,我们在 SwiftUI 4.0 里通过两种不同方式实现了相同的子视图导航功能,任君选择。

感谢观赏,再会!😎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/94318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高效的开发流程搭建

目录 1. 搭建 AI codebase 环境kaggle的服务器1. 搭建 AI codebase 环境 python 、torch 以及 cuda版本,对AI的影响最大。不同的版本,可能最终计算出的结果会有区别。 硬盘:PCIE转SSD的卡槽,, GPU: 软件源: Anaconda: 一定要放到固态硬盘上。 VS code 的 debug功能…

【NLP的python库(03/4) 】: 全面概述

一、说明 Python 对自然语言处理库有丰富的支持。从文本处理、标记化文本并确定其引理开始,到句法分析、解析文本并分配句法角色,再到语义处理,例如识别命名实体、情感分析和文档分类,一切都由至少一个库提供。那么,你…

Pikachu-xxe (xml外部实体注入漏洞)过关笔记

Pikachu-xxe过关笔记 有回显探测是否有回显file:///协议查看本地系统文件php://协议查看php源代码(无法查看当前网页代码,只能看别的)http://协议爆破开放端口(两者的加载时间不同) 无回显第一步第二步第三步 运行结果…

SketchUp Pro 2023 for Mac——打造你的创意之城

SketchUp Pro 2023 for Mac是一款专业级的3D建模软件,为你提供最佳的设计和创意工具。不论你是建筑师、室内设计师,还是爱好者,SketchUp Pro都能满足你对于创意表达的需求。 SketchUp Pro 2023拥有强大而直观的界面,让你轻松绘制…

C++算法 —— 动态规划(10)二维费用背包

文章目录 1、动规思路简介2、一和零3、盈利计划 背包问题需要读者先明白动态规划是什么,理解动规的思路,并不能给刚接触动规的人学习。所以最好是看了之前的动规博客,以及两个背包博客,或者你本人就已经懂得动规了。 1、动规思路简…

Java+Redis:布隆过滤器,打造高效数据过滤神器!

🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是尘缘,一个在CSDN分享笔记的博主。📚📚 👉点击这里,就可以查看我的主页啦!👇&#x…

postgresql16-新特性

postgresql16-新特性 any_value数组抽样数组排序 any_value any_value 返回任意一个值 select e.department_id ,count(*), any_value(e.last_name) from cps.public.employees e group by e.department_id ;数组抽样 -- 从数组中随机抽取一个元素 array_sample(数组&#…

C++:stl:stack、queue、priority_queuej介绍及模拟实现和容量适配器deque介绍。

本文主要介绍c中stl的栈、队列和优先级队列并对其模拟实现,对deque进行一定介绍并在栈和队列的模拟实现中使用。 目录 一、stack的介绍和使用 1.stack的介绍 2.stack的使用 3.stack的模拟实现 二、queue的介绍和使用 1.queue的介绍 2.queue的使用 3.queue的…

插上u盘显示格式化怎么办?U盘数据恢复可以这样做

U盘以其体积小巧、存储容量大、读写速度快的特点,在各种工作和个人使用场合中得到了广泛应用,因此深得用户好评。然而,在日常使用U盘的过程中,经常会遇到一些问题和挑战。 当我需要转移一些资料文件时,总是喜欢使用U盘…

基于spring boot的医疗管理系统 /基于java的医疗系统

摘 要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代&a…

一键智能视频语音转文本——基于PaddlePaddle语音识别与Python轻松提取视频语音并生成文案

前言 如今进行入自媒体行业的人越来越多,短视频也逐渐成为了主流,但好多时候是想如何把视频里面的语音转成文字,比如,录制会议视频后,做会议纪要;比如,网课教程视频,想要做笔记&…

[架构之路-225]:计算机硬件与体系结构 - 分类方法大汇总: RISC, CISC

目录 一、分类方法汇总 二、指令流和数据流的关系分类 三、Flynn 分类:指令并行处理 四、根据指令集架构(ISA)分类 4.1 分类 4.2 开源的RISC-V与封闭的RISC指令集架构比较 4.3 RISC-V的演进路径 4.4 RISC-V与中国芯片自研 4.4 五阶流…

【Kafka专题】Kafka收发消息核心参数详解

目录 前置知识课程内容一、从基础的客户端说起(Java代码集成使用)1.1 消息发送者源码示例1.2 消息消费者源码示例1.3 客户端使用小总结 *二、从客户端属性来梳理客户端工作机制*2.1 消费者分组消费机制2.2 生产者拦截器机制2.3 消息序列化机制2.4 消息分…

PHP 反序列化漏洞:身份标识

文章目录 参考环境访问修饰符访问修饰符PHP 与访问修饰符 手写身份标识身份标识定义身份标识控制字符 NUL在 PHP 中如何表示空字符? 通过空字符尝试构建包含非公共属性对象的序列化文本 空字符的传输控制字符的不可打印性结论另辟蹊径URL 字符编码将非 ASCII 字符文…

指针笔试题(带解析版)

题目2: struct MyStruct {int num;char* pcname;short sdate;char cha[2];short sba[4]; }*p; //结构体大小为32字节 //p0x100000 int main() {p 0x100000;printf("%p\n", p 0x1);//p:结构体指针,1下一个结构体指针,…

617. 合并二叉树

给你两棵二叉树: root1 和 root2 。 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠&#…

Nginx在CentOS上的安装部署、RabbitMQ在CentOS上安装部署

目录 1. Nginx在CentOS上的安装部署 1.1 Nginx简介 1.2 Nginx安装 1.2.1 安装yum依赖程序 1.2.2 手动添加,nginx的yum仓库 1.2.3 通过yum安装最新稳定版的nginx 1.2.4 启动 1.2.5 配置防火墙放行 1.2.6 启动后浏览器输入Linux服务器的IP地址或主机…

最新AI创作系统/AI绘画系统/ChatGPT系统+H5源码+微信公众号版+支持Prompt应用

一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图…

postgresql-聚合函数增强功能

postgresql-聚合函数增强功能 按季度统计入职员工 按季度统计入职员工 select -- extract截取,按季度进行统计入职员工总数 extract(year from hire_date), count(*) filter(where extract(quarter from hire_date) 1) "第一季度", count(*) filter(wh…

一文拿捏分布式协调Redis客服端-Redisson

Redisson 1.介绍 Redisson - 是一个高级的分布式协调Redis客服端 , 专注于分布式系统开发,让用户可以在分布式系统中很方便的去使用Redis。 2.相关使用 1.加锁 //底层是lua脚本保证了加锁的原子性 // 一直等待获取锁,直到获取到锁为止! 默认锁的存活…