SwiftUI 撸码常见错误 2 例漫谈

在这里插入图片描述

概述

在 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 = 0var 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 = 0var 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 worriesvar 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.mzph.cn/diannao/65846.shtml

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

相关文章

【Unity】 HTFramework框架(五十八)【进阶篇】资源及代码热更新实战演示(Deployment + HybridCLR)

更新日期&#xff1a;2025年1月2日。 Github源码&#xff1a;[点我获取源码] 索引 资源及代码热更新实战演示运行演示Demo1.克隆项目工程2.更新子模块3.打开项目4.打开入口场景5.设置远端资源服务器地址6.导入HybridCLR7.初始化HybridCLR8.发布项目9.部署资源版本10.运行Exe11.…

(五)人工智能进阶:基础概念解释

前面我们介绍了人工智能是如何成为一个强大函数。接下来&#xff0c;搞清损失函数、优化方法和正则化等核心概念&#xff0c;才能真正驾驭它&#xff01; 1. 什么是网络模型&#xff1f; 网络模型就像是一个精密的流水线工厂&#xff0c;由多个车间&#xff08;层&#xff0…

初学STM32 --- 外部SRAM

SRAM简介 静态随机存取存储器&#xff08;Static Random-Access Memory&#xff0c;SRAM&#xff09; 1M字节容量的SRAM芯片XM8A51216为例介绍。 SRAM特性: 高速&#xff1a;具有最高访问速度15ns 低功耗&#xff1a;80MHz时55mA&#xff0c;待机电流 20mA TTL电平兼容 …

Zabbix:自动发现功能讲解,包括网络发现、自动注册、低级别自动发现以及案例分享。

ZBX&#xff1a;自动发现功能讲解 视频讲解&#xff1a;Zabbix 自动发现网络发现概述操作方法 自动注册概述操作方法 低级别自动发现概述工作原理及工作流程案例1&#xff0c;base进程监控要求&#xff1a;步骤&#xff1a; 案例2&#xff0c;磁盘IO监控要求&#xff1a;步骤&a…

Windows上安装Go并配置环境变量(图文步骤)

前言 1. 本文主要讲解的是在windows上安装Go语言的环境和配置环境变量&#xff1b; Go语言版本&#xff1a;1.23.2 Windows版本&#xff1a;win11&#xff08;win10通用&#xff09; 下载Go环境 下载go环境&#xff1a;Go下载官网链接(https://golang.google.cn/dl/) 等待…

#端云一体化开发# #HarmonyOS Next#《说书人》鸿蒙原生基于角色的对话式文本编辑开发方案

1、写在前面 过去的一百年里&#xff0c;在“编程”的这个行业诞生之初&#xff0c;人们采用面向过程的方式进行开发&#xff0c;但是&#xff0c;伴随着程序规模的日益增大&#xff0c;程序的复杂度也随之增加&#xff0c;使用结构化编程方法来管理复杂的程序逻辑变得越来越困…

xadmin后台首页增加一个导入数据按钮

xadmin后台首页增加一个导入数据按钮 效果 流程 1、在添加小组件中添加一个html页面 2、写入html代码 3、在urls.py添加导入数据路由 4、在views.py中添加响应函数html代码 <!DOCTYPE html> <html lang

【AimRT】现代机器人通信中间件 AimRT

目录 一、什么是AimRT二、AimRT与ROS22.1 定位与设计2.2 组成与通信方式对比 三、AimRT基本概念3.1 Node、Pkg 和 Module3.2 Protocol、Channel、Rpc 和 Filter3.3 App模式 和 Pkg模式3.4 Executor3.5 Plugin 一、什么是AimRT AimRT 是智元机器人公司自主研发的一款机器人通信…

mysql系列7—Innodb的redolog

背景 本文涉及的内容较为底层&#xff0c;做了解即可&#xff0c;是以前学习《高性能Mysql》和《mysql是怎样运行的》的笔记整理所得。 redolog(后续使用redo日志表示)的核心作用是保证数据库的持久性。 在mysql系列5—Innodb的缓存中介绍过&#xff1a;数据和索引保存在磁盘上…

C++【内存管理】

C/C中程序的内存划分&#xff1a; 栈&#xff1a;又称堆栈&#xff0c;存放非静态的局部变量、函数参数、返回值等等&#xff0c;栈是向下增长的。内存映射段&#xff1a;是高效的&#xff29;&#xff0f;&#xff2f;映射方式&#xff0c;用于装载一个共享的动态内存库。用户…

手机租赁平台开发助力智能设备租赁新模式

内容概要 手机租赁平台开发&#xff0c;简单说就是让你用得起高大上的智能设备&#xff0c;不管是最新款的手机、平板&#xff0c;还是那些炫酷的智能耳机&#xff0c;这个平台应有尽有。想要体验但又不希望花大钱&#xff1f;那你就找对地方了&#xff01;通过灵活的租赁方案…

【开源免费】基于SpringBoot+Vue.JS校园社团信息管理系统(JAVA毕业设计)

本文项目编号 T 107 &#xff0c;文末自助获取源码 \color{red}{T107&#xff0c;文末自助获取源码} T107&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【鸿蒙NEXT】鸿蒙里面类似iOS的Keychain——关键资产(@ohos.security.asset)实现设备唯一标识

前言 在iOS开发中Keychain 是一个非常安全的存储系统&#xff0c;用于保存敏感信息&#xff0c;如密码、证书、密钥等。与 NSUserDefaults 或文件系统不同&#xff0c;Keychain 提供了更高的安全性&#xff0c;因为它对数据进行了加密&#xff0c;并且只有经过授权的应用程序才…

使用npm包的工程如何引入mapboxgl-enhance/maplibre-gl-enhance扩展包

作者&#xff1a;刘大 前言 在使用iClient for MapboxGL/MapLibreGL项目开发中&#xff0c;往往会对接非EPSG:3857坐标系的地图&#xff0c;由于默认不支持&#xff0c;因此需引入mapboxgl-enhance/maplibre-gl-enhance扩展包。 在使用Vue等其他框架&#xff0c;通过npm包下载…

应急指挥系统总体架构方案

引言 应急指挥系统总体架构方案旨在构建一个高效、智能的应急管理体系&#xff0c;以应对自然灾害、事故灾难等突发事件&#xff0c;保障人民生命财产安全。 背景与挑战 近年来&#xff0c;安全生产形势严峻&#xff0c;自然灾害事故频发&#xff0c;对应急指挥系统的要求越…

如何用CSS3创建圆角矩形并居中显示?

在网页设计中&#xff0c;圆角矩形因其美观和现代感而被广泛使用&#xff0c;居中显示元素也是一个常见的需求。今天&#xff0c;我们将学习如何使用CSS3的border-radius属性来创建圆角矩形&#xff0c;并将其居中显示在页面上。 如果你正在学习CSS&#xff0c;那么这个实例将非…

UE5通过蓝图节点控制材质参数

通过蓝图节点控制材质的参数 蓝图节点 在材质上设置标量值 和 在材质上设置向量参数值 Set Scalar Parameter Value on Materials Set Vector Parameter Value on Materials 这两个蓝图节点都可以在蓝图中&#xff0c;控制材质的参数值和向量值

canvas+fabric实现时间刻度尺(二)

前言 我们前面实现了时间刻度尺&#xff0c;鼠标移动显示时间&#xff0c;接下来我们实现鼠标点击某个时间进行弹框。 效果 实现 1.监听鼠标按下事件 2.编写弹框页面 3.时间转换 <template><div><canvas id"rulerCanvas" width"1200"…

手机实时提取SIM卡打电话的信令声音-双卡手机来电如何获取哪一个卡的来电

手机实时提取SIM卡打电话的信令声音 --双卡手机来电如何获取哪一个卡的来电 一、前言 前面的篇章《手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案》中&#xff0c;我们论述了局域网SIP坐席通过手机外呼出去时&#xff0c;手机中主副卡的呼叫调度策略。 但…

离线语音识别+青云客语音机器人(幼儿园级别教程)

1、使用步骤 确保已安装以下库&#xff1a; pip install vosk sounddevice requests pyttsx3 2、下载 Vosk 模型&#xff1a; 下载适合的中文模型&#xff0c;如 vosk-model-small-cn-0.22。 下载地址&#xff1a; https://alphacephei.com/vosk/models 将模型解压后放置在…