SwiftUI 撸码常见错误 2 例漫谈

news2025/1/7 22:20:45

在这里插入图片描述

概述

在 SwiftUI 日常撸码过程中,头发尚且还算茂盛的小码农们经常会犯这样那样的错误。虽然犯这些错的原因都很简单,但有时想要快速准确的定位它们却并不容易。

在这里插入图片描述

况且这些错误还可能在模拟器和 Xcode 预览(Preview)表现的行为不甚一致,这无疑加大了“驯服”它们的难度。

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

  • 概述
  • 1. TabView 每个标签页 tag 类型需要谨慎对待
  • 2. 视图中 @FetchRequest 定义不完整导致的崩溃
  • 总结

本文出现的问题在 Xcode 16.1 + iOS 18.1 中仍会存在。

相信学完本课后,小伙伴们倘若以后再遇到与此类似的问题,必将胸有成竹、迎刃而解。

那还等什么呢?Let‘s fix it!!!😉


本文对应的视频课在此,欢迎小伙伴们恣意观赏:

SwiftUI 撸码常见错误 2 例漫谈


1. TabView 每个标签页 tag 类型需要谨慎对待

在下面这个简单的例子中,点击 TabView 内部每个标签页竟然毫无反应!

在这里插入图片描述

下面上源代码,你能看出是哪里的问题吗?

enum TabTag: Int {
    case home = 0, worry, game, user
}

@available(iOS 17, *)
@Observable
class Model {
    var currentSelectingTab = 0
}

@available(iOS 17.0, *)
struct Main: View {
    
    @State var model = Model()
    
    var body: some View {
        NavigationStack {
            TabView(selection: $model.currentSelectingTab) {
                Text("Home")
                    .tabItem {
                        Label("Home", systemImage: "house")
                    }
                    .tag(TabTag.home)
                
                Text("Worry")
                    .tabItem {
                        Label("Worry", systemImage: "figure.fall")
                    }
                    .tag(TabTag.worry)
                
                Text("Game")
                    .tabItem {
                        Label("游戏", systemImage: "gamecontroller.fill")
                    }
                    .tag(TabTag.game)
                
                Text("User")
                    .tabItem {
                        Label("用户", systemImage: "person.fill")
                    }
                    .tag(TabTag.user)
            }
            .navigationTitle("SwiftUI 初学者常见错误")
        }        
    }
}

其实原因很简单:问题就出在 TabView 中每个标签页的 tag 类型上。我们实际使用的是 TabTag 类型,但是向 TabView 构造器 selection 形参绑定的却是整形类型。

所以这个问题解决起来也很容易,只需要将 Model 中对应的属性改为 TabTag 类型即可:

@available(iOS 17, *)
@Observable
class Model {
    var currentSelectingTab = TabTag.home
}

再次运行代码一切都回归正常了。

在这里插入图片描述

但是故事到这里并没有结束。假如我们将代码修改为如下形式,却是能够选择 TabView 中每个标签页的:

struct Main: View {
    @State var currentSelectingTab = 0
    
    var body: some View {
        NavigationStack {
            TabView(selection: $currentSelectingTab) {
                Text("Home")
                    .tabItem {
                        Label("Home", systemImage: "house")
                    }
                    .tag(TabTag.home)
                
                Text("Worry")
                    .tabItem {
                        Label("Worry", systemImage: "figure.fall")
                    }
                    .tag(TabTag.worry)
                
                Text("Game")
                    .tabItem {
                        Label("游戏", systemImage: "gamecontroller.fill")
                    }
                    .tag(TabTag.game)
                
                Text("User")
                    .tabItem {
                        Label("用户", systemImage: "person.fill")
                    }
                    .tag(TabTag.user)
            }
        }
    }
}

在上面的代码中,我们仅仅将原来在 Model 中 Int 类型的 currentSelectingTab 直接放到视图 @State 中,并将其与 TabView 的选中操作绑定起来而已。但是这样的话,同样造成了 TabView 标签页 tag 类型的不一致,为何又没有问题呢?

其实,这波“走位”表面看起来貌似可以恣意切换 TabView 各个标签页,但实际却是有问题的。为了拨开迷雾见青天,我们特地为 currentSelectingTab 状态增加了 onChange 监听器:

struct Main: View {
    @State var currentSelectingTab = 0
    
    var body: some View {
        NavigationStack {
            TabView(selection: $currentSelectingTab) {
                //...
            }
        }
        .onChange(of: currentSelectingTab) {_,new in
            // 永远不会进入此闭包中
            print("\(new)")
        }
    }
}

运行代码可以发现:尽管我们可以切换到不同标签页中,但我们的 currentSelectingTab 状态却从未发生过改变!

所以,最终我们发现了 SwiftUI 中一个“不一致”的场景:在 @Observable 对象中属性类型和 TabView 标签页 tag 不匹配会导致“正确”的交互行为(标签页无法切换),但在 @State 同样的属性却不能。

希望苹果在将来可以将它们一致化。

2. 视图中 @FetchRequest 定义不完整导致的崩溃

另一个隐蔽的问题涉及到 CoreData 为 SwiftUI 视图添加的 @FetchRequest 属性包装器。

@available(iOS 17, *)
struct WorriesView: View {    
    @FetchRequest(sortDescriptors: [.init(keyPath: \Worry.occurrenceTime, ascending: false)]) var worries
        
    var body: some View {
        NavigationStack {
            List {
                Section("最近担忧") {}
                
                Section("严重担忧") {}
                                
                Section("其它担忧") {}
            }
            .navigationTitle("担忧终结者")
        }
    }
}

上面的代码貌似人畜无害,而且在 Xcode 预览中的显示也是无懈可击:

在这里插入图片描述

不过,如果我们编译并胆敢在模拟器或真机上运行上述代码,App 就会立即崩溃:

在这里插入图片描述

而且从提示来看,很难发现究竟是哪里出了问题。如果不是我们已将问题局限在 @FetchRequest 那行代码上,大家恐怕很难轻易找出罪魁祸首,更何况如果它匿影藏形隐身在海量视图中了。

经常在 SwiftUI 中使用 @FetchRequest 属性修饰器来获取 CoreData 数据的小伙伴们,应该能一眼看出上面代码中的问题,实际上它缺少了 FetchedResults 后半部分的定义,是不完整的:

@FetchRequest(sortDescriptors: 
[.init(keyPath: \V3_Worry.occurrenceTime, ascending: false)]) 
var worries: FetchedResults<V3_Worry>

只要将其补全即可。

该问题的另一个特点是它在 Xcode 预览中讳莫如深、深藏不露,只为在实际运行时给秃头码农们“当头一棒”,实属可恨!

不过,通过上面条分缕析的介绍,现在小伙伴们对它们一定能够火眼金睛、无所畏惧,棒棒哒!💯


更多 Xcode 预览调试中的技巧和陷阱,请小伙伴们移步如下链接观赏精彩的内容:

  • Xcode编写SwiftUI代码时一个编译通过但导致预览(Preview)崩溃的小陷阱
  • Xcode如何在预览(Preview)调试中避免与SwiftUI正常运行时环境不一致导致的崩溃
  • Xcode预览(Preview)显示List视图内容的一个Bug及解决
  • Xcode 15 预览 SwiftUI 视图中 @FetchRequest 查询结果不能正确刷新的解决

总结

在本篇博文中,我们讨论了 Xcode 16.1(iOS 18.1)中仍然存在 SwiftUI 的两个“鸱张鼠伏”、较难发现缘由小问题的“症状”和解决之道,希望可以帮助到大家。

感谢观赏,再会啦!😎

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

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

相关文章

linux系统(ubuntu,uos等)连接鸿蒙next(mate60)设备

以前在linux上是用adb连接&#xff0c;现在升级 到了鸿蒙next&#xff0c;adb就不好用了。得用Hdc来了&#xff0c;在windows上安装了hisuit用的好好的&#xff0c;但是到了linux(ubuntu2204)下载安装了 下载中心 | 华为开发者联盟-HarmonyOS开发者官网&#xff0c;共建鸿蒙生…

2024年12月HarmonyOS应用开发者高级认证全新题库

注意事项&#xff1a;切记在考试之外的设备上打开题库进行搜索&#xff0c;防止切屏三次考试自动结束&#xff0c;题目是乱序&#xff0c;每次考试&#xff0c;选项的顺序都不同&#xff0c;作者已于2024年12月15日又更新了一波题库&#xff0c;题库正确率99%&#xff01; 新版…

Anaconda安装(2024最新版)

安装新的anaconda需要卸载干净上一个版本的anaconda&#xff0c;不然可能会在新版本安装过程或者后续使用过程中出错&#xff0c;完全卸载干净anaconda的方法&#xff0c;可以参考我的博客&#xff01; 第一步&#xff1a;下载anaconda安装包 官网&#xff1a;Anaconda | The O…

docker中使用Volume完成数据共享

情景概述 在一个docker中&#xff0c;部署两个MySQL容器&#xff0c;假如它们的数据都存储在自己容器内部的data目录中。这样的存储方式会有以下问题&#xff1a; 1.无法保证两个MySQL容器中的数据同步。 2.容器删除后&#xff0c;数据就会丢失。 基于以上问题&#xff0c;容…

Mac软件介绍之录屏软件Filmage Screen

软件介绍 Filmage Screen 是一款专业的视频录制和编辑软件&#xff0c;适用于 Mac 系统 可以选择4k 60fps&#xff0c;可以选择录制电脑屏幕&#xff0c;摄像头录制&#xff0c;可以选择区域录制。同时也支持&#xff0c;简单的视频剪辑。 可以同时录制电脑麦克风声音 标准…

【Redis经典面试题七】Redis的事务机制是怎样的?

目录 一、Redis的事务机制 二、什么是Redis的Pipeline&#xff1f;和事务有什么区别&#xff1f; 三、Redis的事务和Lua之间有哪些区别&#xff1f; 3.1 原子性保证 3.2 交互次数 3.3 前后依赖 3.4 流程编排 四、为什么Lua脚本可以保证原子性&#xff1f; 五、为什么R…

【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 1. 相关排序和查找算法的原理 2. C 类与成员函数的定义 3. 数组作为类的成员变量的处理 4. 函数参数传递与返回值处理 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a; 将直接插入排序、直接选择排序、冒泡…

[开源]自动化定位建图系统

系统状态机&#xff1a; 效果展示&#xff1a; 1、 机器人建图定位系统-基础重定位&#xff0c;定位功能演示 2、 机器人建图定位系统-增量地图构建&#xff0c;手动回环检测演示 3、敬请期待… 开源链接&#xff1a; 1、多传感器融合里程计 https://gitee.com/li-wenhao-lw…

简单的jmeter数据请求学习

简单的jmeter数据请求学习 1.需求 我们的流程服务由原来的workflow-server调用wfms进行了优化&#xff0c;将wfms服务操作并入了workflow-server中&#xff0c;去除了原来的webservice服务调用形式&#xff0c;增加了并发处理&#xff0c;现在想测试模拟一下&#xff0c;在一…

conda/pip基本常用命令理解与整理

最近配置了两轮pytorch环境&#xff0c;由于要频繁用到各种conda和pip命令&#xff0c;所以再此整理一下。 文章目录 前言&#xff1a;conda虚拟环境总结与解读Conda和pip的理解区别和联系命令格式 conda环境命令查看创建和删除导出与导入激活和退出 包管理命令安装和删除文件批…

Maven 详细配置:Maven settings 配置文件的详细说明

Maven settings 配置文件是 Maven 环境的重要组成部分&#xff0c;它用于定义用户特定的配置信息和全局设置&#xff0c;例如本地仓库路径、远程仓库镜像、代理服务器以及认证信息等。settings 文件分为全局配置文件&#xff08;settings.xml&#xff09;和用户配置文件&#x…

Unity-Mirror网络框架-从入门到精通之Chat示例

文章目录 前言Chat聊天室Authentication授权ChatAuthenticatorChat示例中的授权流程聊天Chat最后 前言 在现代游戏开发中&#xff0c;网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架&#xff0c;专为多人游戏开发设计。它使得开发者能够轻…

复杂园区网基本分支的构建

目录 1、各主机进行网络配置。2、交换机配置。3、配置路由交换&#xff0c;进行测试。4、配置路由器接口和静态路由&#xff0c;进行测试。5、最后测试任意两台主机通信情况 模拟环境链接 拓扑结构 说明&#xff1a; VLAN标签在上面的一定是GigabitEthernet接口的&#xff0c…

这是什么操作?强制迁移?GitLab 停止中国区用户访问

大家好&#xff0c;我是鸭鸭&#xff01; 全球知名代码托管平台 GitLab 发布通告&#xff0c;宣布不再为位于中国大陆、香港及澳门地区的用户提供访问服务&#xff0c;并且“贴心”建议&#xff0c;可以访问极狐 GitLab。 极狐 GitLab 是一家中外合资公司&#xff0c;宣称获得…

CDP集成Hudi实战-spark shell

[〇]关于本文 本文主要解释spark shell操作Hudi表的案例 软件版本Hudi1.0.0Hadoop Version3.1.1.7.3.1.0-197Hive Version3.1.3000.7.3.1.0-197Spark Version3.4.1.7.3.1.0-197CDP7.3.1 [一]使用Spark-shell 1-配置hudi Jar包 [rootcdp73-1 ~]# for i in $(seq 1 6); do s…

设计模式学习[15]---适配器模式

文章目录 前言1.引例2.适配器模式2.1 对象适配器2.2 类适配器 总结 前言 这个模式其实在日常生活中有点常见&#xff0c;比如我们的手机取消了 3.5 m m 3.5mm 3.5mm的接口&#xff0c;只留下了一个 T y p e − C Type-C Type−C的接口&#xff0c;但是我现在有一个 3.5 m m 3.…

数据挖掘——数据预处理

数据挖掘——数据预处理 数据预处理数据预处理 ——主要任务数据清洗如何处理丢失的数据如何处理噪声数据如何处理不一致数据 数据集成相关分析相关系数(也成为皮尔逊相关系数)协方差 数据规约降维法&#xff1a;PCA主成分分析降数据——抽样法数据压缩 数据预处理 数据预处理…

Unity-Mirror网络框架-从入门到精通之CCU示例

文章目录 前言什么是CCU&#xff1f;测试结果最后 前言 在现代游戏开发中&#xff0c;网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架&#xff0c;专为多人游戏开发设计。它使得开发者能够轻松实现网络连接、数据同步和游戏状态管理。本文…

如何在 Ubuntu 22.04 上安装 Nagios 服务器教程

简介 在本教程中&#xff0c;我们将解释如何在 Ubuntu 22.04 上安装和配置 Nagios&#xff0c;使用 Apache 作为 Web 服务器&#xff0c;并通过 Let’s Encrypt Certbot 使用 SSL 证书进行保护。 Nagios 是一个强大的监控系统&#xff0c;它可以帮助组织在 IT 基础设施问题影…

大语言模型训练所需的最低显存,联邦大语言模型训练的传输优化技术

联邦大语言模型训练的传输优化技术 目录 联邦大语言模型训练的传输优化技术大语言模型训练所需的最低显存大语言模型训练所需的最低显存 基于模型微调、压缩和分布式并行处理的方法,介绍了相关开源模型及技术应用 核心创新点 多维度优化策略:综合运用基于模型微调、模型压缩和…