消失的它:摆脱 SwiftUI 中“嵌入视图数量不能超过 10 个”限制的秘密

news2024/9/22 4:03:29

在这里插入图片描述

概览

SwiftUI 带来了描述性界面布局的新玩法,让我们可以超轻松的创建复杂应用界面。但是在早期 SwiftUI 中有一个“著名”的限制大家知道么?那就是 @ViewBuilder 中嵌入子视图数量不能超过 10 个!

不过,从 Swift 5.9 开始这一“桎梏”已悄然消失的无影无踪。

在这里插入图片描述

这个限制为什么已然烟消云散?早期的限制又是如何产生的呢?

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

  • 概览
  • 1. 不能超过 10 个,你是来逗我的吗?
  • 2. “值与类型形参包”
  • 3. SwiftUI 的新实现
  • 4. 为何不能用泛型数组?
  • 总结

想知道事件的前因后果么?那还等什么呢?

Let‘s go!!!😉


1. 不能超过 10 个,你是来逗我的吗?

在 Swift 5.5 中增加了 some 关键字,让 SwiftUI 能够用简洁类型来描述海量复合视图。这还不算完,可能  觉得视图组合的手法还是太麻烦,随即又祭出 @ViewBuilder 来进一步简化 SwiftUI 的视图构建。


其实,SwiftUI 视图的 body 计算属性已被 @ViewBuilder 默默修饰着,我们能够轻松自在,全靠 @ViewBuilder 为我们负重前行:

@ViewBuilder var body: Self.Body { get }

更多 ViewBuilder 实现细节的讨论,请小伙伴们移步 Swift 官方社区观赏:

  • SwiftUI @ViewBuilder Result is a TupleView, How is Apple Using It And Able to Avoid Turning Things Into AnyVIew?

@ViewBuilder 其实是结果构建器(Result Builder,Swift 5.4)在 SwiftUI 中的一个实现。结果构建器可以被视为一种嵌入式领域特定语言(DSL),用于将收集的内容组合成最终的结果。

这就是我们可以这样创建 SwiftUI 复合视图的原因:

@ViewBuilder func lot(_ needDetails: Bool) -> some View {
    Text("Hello World")
        .font(.title)
    if needDetails {
        Text("大熊猫侯佩 @ csdn")
            .font(.headline)
            .foregroundStyle(.gray)
    }
}

在这里插入图片描述

那么,ViewBuilder 在内部是如何处理传入不定数量视图的呢?

ViewBuilder 为了满足 Result Builder 的语义,必须实现其规定的一系列方法:

在这里插入图片描述


取决于大家要实现 DSL 语言的完整性和复杂性,我们可以选择实现尽可能少或全部这些方法。

讨论如何用 Result Builder 来实现自己的 DSL 语言超出了本文的范畴,感兴趣的小伙伴们可以移步下面的链接观赏进一步内容:

  • Result builders in Swift explained with code examples

想了解更多 Swift 语言开发的知识,小伙伴们可以到我的专题专栏中进行系统性学习:

  • Swift 语言开发精讲(文章平均质量分 97)

而对于简单 View 的合成,ViewBuilder 竟然采用了一种最“蠢”的方式:为每种“可能”的情况手动定义一个方法。

于是乎,就有了下面这一大坨泛型方法:

在这里插入图片描述

正如小伙伴们所猜的那样,这些方法中最大可传入的参数数量就是 10 (c0-c9),所以这就是“桎梏”的根本原因:我们在 @ViewBuilder 中最多只能包含 10 个子视图。

static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View {
    return .init((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9))
}

对于超过 10 个视图的情况,我们只能“八仙过海各显神通”的尝试绕过它。

比如一种办法是:将 10 个以上的视图塞到多个 Group 中去。

2. “值与类型形参包”

从 Swift 5.9 开始,苹果似乎认识到之前的做法比较“二”,所以推出了新的“值与类型形参包”(Value and Type parameter packs)机制。

该机制专门用于处理不确定数量泛型参数的方法:

func eachFirst<each T: Collection>(_ item: repeat each T) -> (repeat (each T).Element?) {
    return (repeat (each item).first)
}

比如在上面代码中,我们用 each 和 repeat each 分别修饰了泛型参数的形参和结果部分。

eachFirst() 方法的作用是将所有传入集合的第一个元素组成一个新的元组。现在 eachFirst() 泛型方法可以接受任意个类型为 Collection 的参数,同时返回同样数量 Collection.Element? 类型元素的元组。

我们可以这样调用 eachFirst() 方法:

let numbers = [0, 1, 2]
let names = ["Antoine", "Maaike", "Sep"]
let firstValues = eachFirst(numbers, names)
print(firstValues) 
// (Optional(0), Optional("Antoine"))

看到了么?不管传入参数有多少个、不管它们是什么类型(至少必须是 Collection),eachFirst() 方法都可以正常工作。

有了“值与类型形参包”,我们处理泛型方法的灵活性提升一个新层级!

3. SwiftUI 的新实现

在 Swift 5.9 中,SwiftUI 用新“值与类型形参包”机制重写了 ViewBuilder 的实现。

不像之前每种情况“傻傻的”写一个对应的 buildBlock() 方法,现在只需一个带 each/repeat each 的 buildBlock() 方法足矣:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@resultBuilder public struct ViewBuilder {

    public static func buildBlock<each Content>(_ content: repeat each Content) -> TupleView<(repeat each Content)> where repeat each Content : View
}

如上代码所示,我们现在可以向 @ViewBuilder 传递任意数量的视图了:

struct ContentView: View {
    var body: some View {
        Group {
            Text("1")
            Text("2")
            Text("3")
            Text("4")
            Text("5")
            Text("6")
            Text("7")
            Text("8")
            Text("9")
            Text("10")
            Text("11")
            Text("12")
        }
        .foregroundStyle(.white)
        .background {
            Circle()
                .fill(Color.blue.opacity(0.5))
                .frame(width: 35)
        }
        .shadow(radius: 5.0)
        .padding()
        .font(.title2.weight(.bold))
    }
}

是不是很赞呢?棒棒哒💯

4. 为何不能用泛型数组?

有些小伙伴可能觉得,为什么之前 eachFirst() 方法不能用泛型数组的方式来实现呢?用泛型数组不就可以传入任意数量的集合参数了吗?

我们来试一下:

func eachFirst<T: Collection>(collections: [T]) -> [T.Element?] {
    collections.map(\.first)
}

实际运行就会发现,如果用泛型数组则无法传入不同类型元素的集合:

在这里插入图片描述

这就是为什么上面代码报错的原因了。

有时候我们希望 eachFirst() 泛型方法中至少要带一个形参,这可以用类似下面的方式来实现:

func eachFirst<FirstT: Collection, each T: Collection>(_ firstItem: FirstT, _ item: repeat each T) -> (FirstT.Element?, repeat (each T).Element?) {
    return (firstItem.first, repeat (each item).first)
}

let numbers = [0, 1, 2]
let names = ["Antoine", "Maaike", "Sep"]
let booleans = [true, false, true]
let doubles = [3.3, 4.1, 5.6]

let firstValues = eachFirst(numbers, names, booleans, doubles)
print(firstValues) 
// (Optional(0), Optional("Antoine"), Optional(true), Optional(3.3))

总结

在本篇博文中,我们讨论了 SwiftUI 中“嵌入视图数量不能超过 10 个”这一限制的原因,并介绍了从 Swift 5.9+ 开始这一限制为什么最终消失了?

感谢观赏,再会!😎

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

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

相关文章

PHP判断扫码支付扫码条码支付宝微信区分

微信&#xff1a;用户付款码规则&#xff1a;18位纯数字&#xff0c;前缀以10、11、12、13、14、15开头 支付宝&#xff1a;25~30开头的长度为16~24位的数字&#xff0c;实际字符串长度以开发者获取的付款码长度为准 <?php /*** 判断扫码支付的方式* param string $code 扫…

前后端分离项目为什么很火?有什么优势?

目录 一、什么是前后端分离 二、前后端分离项目的技术栈 三、前后端分离项目有什么优势 一、什么是前后端分离 前后端分离是一种软件架构的设计模式,它将应用程序的前端&#xff08;即用户界面&#xff09;和后端&#xff08;即服务器端&#xff09;进行解耦,使得它们可以独…

HTML简单介绍

且视他人之疑目如盏盏鬼火&#xff0c;大胆地去你的夜路。 1.网页 组成&#xff1a;文字&#xff0c;图片&#xff0c;音频&#xff0c;视频&#xff0c;超链接 2.Web标准 3.HTML 超文本标记语言 3.1HTML结构 网页可以看成是一篇文章 如&#xff1a;整体&#xff0c;头部…

【KVM-4】硬件虚拟化技术(详)

前言 大家好&#xff0c;我是秋意零。 经过前面章节的介绍&#xff0c;已经知道KVM虚拟化必须依赖于硬件辅助的虚拟化技术&#xff0c;本节就来介绍一下硬件虚拟化技术。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平…

劲升逻辑与青岛港国际集装箱发展有限公司签署合作意向书,合力打造贸易互联互通新高地

合作意向书签署现场 2023 年 11 月 11 日&#xff0c;中国山东——跨境贸易数字化领域的领导者劲升逻辑与山东港口青岛港子公司青岛港国际集装箱发展有限公司在新加坡-山东经贸理事会&#xff08;简称“新鲁理事会”&#xff09;全体会议期间正式签署合作意向书&#xff0c;双…

2024怎么自学软件测试?自动化测试?测试老鸟总结,少走弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、自学软件测试怎…

IDEA软件使用步骤

1.IDEA概述 IDEA全称InelliJ IDEA,是用于java语言开发的集成环境&#xff0c;它是业界公认的目前用于Java程序开发最好的工具。 集成环境&#xff1a;把代码编写&#xff0c;编译&#xff0c;执行&#xff0c;调试扽过多种功能综合到一起的开发工具。 下载&#xff1a;https…

【OpenVINO】基于 OpenVINO C# API 部署 RT-DETR 模型

基于 OpenVINO C# API 部署 RT-DETR 模型 1. RT-DETR2. OpenVINO3. 环境配置4. 模型下载与转换5. C#代码实现5.1 模型推理类实现1. 模型推理类初始化2. 图片预测API 5.2 模型数据处理类RTDETRProcess1. 定义RTDETRProcess2. 输入数据处理方法3. 预测结果数据处理方法 6. 预测结…

音频url如何下载到本地浏览器上

音频url如何下载到本地浏览器上 一、代码 一、代码 this.downloadFile(url, name)downloadFile(url, filename) {const xhr new XMLHttpRequest()xhr.open(GET, url, true)xhr.responseType blobxhr.onload function () {if (xhr.status 200) {const blob new Blob([xhr.r…

VR全景技术在城市园区发展中有哪些应用与帮助

引言&#xff1a; 在数字化时代的浪潮中&#xff0c;虚拟现实&#xff08;VR&#xff09;全景技术逐渐融入各个领域&#xff0c;也为城市园区展示带来了全新的可能性。 一&#xff0e;VR全景技术简介 虚拟现实全景技术是一种通过全景图像和视频模拟真实环境的技术。通过相关设…

ultrascale+mpsoc系列的ZYNQ中DDR4参数设置说明

ultrascalempsoc系列的ZYNQ中DDR4参数设置说明 标题1 概述标题2 讲述平台标题3 ZYNQ的DDR设置界面参数标题4 DDR参数界面说明如下 标题1 概述 本文用于讲诉ultrascalempsoc系列中的ZYNQ的DDR4的参数设置与实际硬件中的DDR选型之间的关系&#xff0c;为FPGA设计人员探明道路。 …

thinkphp8 数据库的连接

账号&#xff1a;root 密码&#xff1a;自己设置 http://localhost:888/index.php当出现这个并且能登陆就算成功了。 回到项目config/database.php .env 里面&#xff08;如果已经.example.env 改成了.env,则改下边&#xff0c;db_name改成你的数据库表名&#xff09; 多个…

响应式摄影科技传媒网站模板源码带后台

模板信息&#xff1a; 模板编号&#xff1a;540 模板编码&#xff1a;UTF8 模板颜色&#xff1a;黑白 模板分类&#xff1a;摄像、婚庆、家政、保洁 适合行业&#xff1a; 模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#xff0c;原创设计、手…

使用SpringAOP+Redis实现接口处理幂等

文章目录 一、思路分析二、代码实战1、搭建SpringbootAOPRedis环境2、自定义注解3、切面类4、测试一下吧 一、思路分析 在调用后台接口时&#xff0c;由于用户多次点击或者说第三方重试&#xff0c;可能会导致幂等问题。 解决方案无非就是上一次请求没有处理完&#xff0c;第…

使用Tipas结合内网穿透在Ubuntu上搭建高效问题解答平台网站

文章目录 前言2.Tipask网站搭建2.1 Tipask网站下载和安装2.2 Tipask网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 4. 公网访问测试5. 结语 前…

第八章 :如何基于Spring Boot +Mybatis 快速开发 Restful API

第八章 :如何基于Spring Boot +Mybatis 快速开发 Restful API 前言 本章知识重点:主要讲解开发人员如何利用【MybatisPlus+EasyCode插件 】快速开发Restful API ,利用节约的时间学习,养成一种正向循环的技术之道,最后达到终身学习成长! 案例基于SpringBoot 2.3.2.RELEASE…

Outlook邮件视图设置怎么修复

故障现象 Outlook邮箱显示不对 故障截图 故障原因 邮箱视图设置不对 解决方案 1、在Outlook上方工具栏找到视图按钮&#xff0c;以此选择视图→视图设置→列&#xff0c;打开选择的列 2、在视图→邮件预览里面&#xff0c;选择1行&#xff0c;在阅读格式选择靠右&#xff…

站长必读:如何巧妙应对网站攻击与提升速度

亲爱的站长们&#xff0c;您是否曾为网站被攻击而烦恼&#xff0c;或者一直想让您的网站更快速地响应用户&#xff1f;别担心&#xff0c;本文将为您揭示一项重要而有效的解决方案——CDN&#xff08;内容分发网络&#xff09;。让我们一起探讨如何从站长的角度出发&#xff0c…

【深度学习实验】网络优化与正则化(三):随机梯度下降的改进——Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入必要的库1. 随机梯度下降SGD算法a. PyTorch中的SGD优化器b. 使用SGD优化器的前馈神经网络 2.随机梯度下降的改进方法a. 学习率调整b. 梯度估计修正 3. 梯度估计修正&#xff1a;动量法Momen…

Vue使用高德地图实现点击获取经纬度以及搜索功能

1. 首先在高德开放平台申请key值 2. 然后会在这个地方显示 3. 在VScode里面安装地图 yarn add amap/amap-jsapi-loader --save 4. 准备一个容器 <div id"maps"></div> <style scoped>#maps {width: 100%;height: 100%;position: relative;z-index…