SwiftUI 6.0(iOS 18)新容器视图修改器漫谈

在这里插入图片描述

概览

本届 WWDC 2024 观影正如火如荼的进行中,一片鸟语花香、枝繁叶茂的苹果树上不时结出几颗令人垂涎欲滴的美味苹果让秃头码农们欲罢不能。

在这里插入图片描述

如您所愿,在界面布局“利器” SwiftUI 这根蔓藤也长出不少喜人的果实,其中在 iOS 18.0 中新添加的容器视图修改器大家一定不能错过。

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

  • 概览
  • 1. 探囊取物:获取容器子视图
  • 2. 聚沙成塔:重新组织容器子视图
  • 总结

SwiftUI 6.0 新容器视图修改器让我们得以分解原本“铁板一块”的“黑箱”视图,打开无限可能。

无需等待,让我们马上开始探寻之旅吧!

Let’s go!!!😉


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

SwiftUI 6.0(iOS 18)新容器视图修改器漫谈


1. 探囊取物:获取容器子视图

在 SwiftUI 6.0(iOS 18)之前,如果我们希望让自定义容器视图处理布局排版的细节则需要知道数据集,并根据数据集中每个单独元素构建对应的容器子视图:

import SwiftUIstruct MyContainerView<Content: View>: View {var items: [Int]@ViewBuilder var itemView: (Int) -> Contentvar body: some View {List {ForEach(items, id: \.self) { item initemView(item)}}}
}struct ContentView: View {var body: some View {MyContainerView(items: Array(1...10)) { item inText("Item \(item)").font(.title).padding()}}
}

如上代码所示,我们将容器数据集合 items 和单个数据对应的子视图闭包 itemView() 从父视图传入到了 MyContainerView 中。

在这里插入图片描述

不过在某些情况下,我们希望在父视图中更灵活的创建容器子视图,比如以静态与动态相结合的方式:

struct ContentView: View {var body: some View {MyContainerView {Text("Item Header")Text("Item Subheader")ForEach(1...10, id: \.self) { item inText("Item \(item)")}Text("Item Tail")}}
}

如上代码所示,我们试图将 3 个静态和 10 个动态产生的子视图和睦融洽的一起融入到容器视图 MyContainerView 中。但不幸的是,这在 SwiftUI 6.0 之前几乎是不可能完成的任务。


当然,如果我们巧妙运用一些 Swift Mirror “黑魔法”的话也不是绝对不可能。想要进一步了解 Mirror 黑魔法解决之道的小伙伴们,请移步如下链接观赏精彩的内容:

  • SwiftUI 打造一款收缩自如的 HStack(三):“魔镜魔镜,我爱你”

究其原因则是因为:在 SwiftUI 6.0 之前我们无法探查一个 View 的内部结构,它对我们来说就是一个彻头彻尾的“黑盒”视图。

不过从 SwiftUI 6.0(iOS 18)开始情况有了改观,苹果新增了若干容器视图修改器为我们排愁解忧。其中 ForEach(subviewOf:) 修改器方法就是这里我们所需要的那个“救世主”。

在这里插入图片描述

利用 ForEach(subviewOf:) 方法我们可以将 MyContainerView 容器视图修改为如下模样:

struct MyContainerView<Content: View>: View {@ViewBuilder var content: Contentvar body: some View {List {ForEach(subviewOf: content) {subview insubview.padding().frame(maxWidth: .infinity).background(.green.gradient)}}}
}

从上面修改后的代码中可以看到:现在我们只需专注于子视图本身,而无需再操心对应的子元素 Item 了。

在这里插入图片描述

除了 ForEach(subviewOf:) 以外,SwiftUI 6.0 还新增了一个类似的 ForEach(sectionOf: ) 修改器方法,如果小伙伴们希望自己的容器支持 Section 布局则会寻求它的鼎力相助哦。

2. 聚沙成塔:重新组织容器子视图

在聊完了将一整块黑盒视图从布局上分解为各个独立的子视图后,我们再来看看容器视图的另一种操作:将容器子视图重新“恣意”组合在一起。

假如我们希望自己的容器视图能够进一步根据子视图的数量或其它特定条件来布局界面,那么新的 Group(subviewsOf:) 修改器你绝对不能错过:

在这里插入图片描述

Group(subviewsOf: content) 方法让我们可以从整个容器的所有子视图而不是单个子视图来考虑布局大计:

struct HyListView<Content: View>: View {@ViewBuilder var content: Contentvar body: some View {VStack {Group(subviewsOf: content) { subviews inif subviews.isEmpty {Text("🐼No\nContent").font(.system(size: 100)).padding()} else {if let first = subviews.first {first.font(.largeTitle.weight(.heavy)).background(Circle().foregroundStyle(.blue.gradient).frame(width: 150, height: 150))}if subviews.count >= 3 {HStack {subviews[1]subviews[2]}.font(.title).foregroundStyle(.white).padding().background(.green)if subviews.count > 3 {List {subviews[3...].frame(maxWidth: .infinity).padding().background(.yellow.gradient)}.listStyle(.plain).font(.title3.weight(.black)).foregroundStyle(.red).padding()}}}}.transition(.slide)}}
}struct ContentView: View {@State var count = 10.0var items: [Int] {if count == 0 {[]} else {Array(0...Int(count))}}var body: some View {VStack {HyListView {ForEach(items, id: \.self) { i inText("Item \(i)")}}.animation(.bouncy, value: count)Spacer()HStack {Text("\(Int(count))").fontWeight(.heavy)Slider(value: $count, in: 0...20.0, label: {}, minimumValueLabel: {Text("0")}, maximumValueLabel: {Text("20")}).foregroundStyle(.gray)}.padding()}}
}

在上面代码中,我们创建了自己的 HyListView 容器视图,并且根据实际子视图的数量利用 Group(subviewsOf: content) 修改器方法来决定到底如何将它们“浑然天成”的组合在一起。

在这里插入图片描述

从实际运行效果可以看到,我们动态的根据容器内部的子视图来决定到底如何布局容器本身,再辅以动画整个效果简直 Nice 的不要不要的:

在这里插入图片描述

现在小伙伴们手握 SwiftUI 6.0 中新的容器视图修改“利器”,在 App 的开发中是不是愈发感觉神采奕奕、容光焕发了呢?棒棒哒!

总结

在本篇博文中,我们讨论了 WWDC24 里 SwiftUI 6.0(iOS 18)中最新的容器视图修改器,并用简单的示例代码让小伙伴们豁然开朗!

感谢观赏,再会!😎

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

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

相关文章

rabbitMQ的简单使用

rabbitMQ的介绍 RabbitMQ是一个开源的消息代理和队列服务器&#xff0c;主要用于在不同的应用程序之间传递消息。它基于AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议&#xff0c;提供了一种可靠的方式来处理异步通信。RabbitMQ使用Erlang语言编写&…

springboot 整合redis问题,缓存击穿,穿透,雪崩,分布式锁

boot整合redis 压力测试出现失败 解决方案 排除lettuce 使用jedis <!-- 引入redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclus…

内存泄漏 内存溢出

概念 内存泄漏&#xff1a;是程序没有正确的释放已分配的内存&#xff0c;造成系统内存的浪费。内存泄漏很难发现&#xff0c;因为他不会直接导致程序崩溃&#xff0c;而是会慢慢降低程序的性能。 内存溢出&#xff1a;系统中存在无法回收的内存或使用的内存过多&#xff0c;…

【linux-imx6ull-定时器与中断】

目录 1. 前言2. Linux软件定时器2.1 内核频率选择2.2 重要的API函数2.3 Linux软件定时器的使用配置流程 4. Linux中断4.1 简单中断使用4.1.1 简要说明4.1.2 重要的API函数4.1.3 中断的简要配置流程 4.2. 中断的上半部和下半部4.2.1 tasklet实现下半部4.2.2 work实现下半部 1. 前…

pytorch 使用GPU加速常见的问题

pytorch如何使用gpu加速 print(torch.cuda.is_available()) # 设置gpu设备 device torch.device(cuda:0 if torch.cuda.is_available() else cpu) # net使用GPU net.to(device)# 数据copy到gpu inputData inputData.to(device)# 开始执行 ... net(inputData) ...两种方式&am…

MySQL数据操作与查询- 聚合函数和分组查询

一、聚合函数 聚合函数主要用来进行数据 汇总 。 1、sum 返回选取的某列的总和。 语法&#xff1a; select sum(字段名) from 表名 where 条件表达式 2、max 返回选取的某列的最大值。 语法&#xff1a; select max(字段名) from 表名 where 条件表达式 3、min 返…

【网络安全的神秘世界】AppScan安装及使用指南

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 https://www.hcl-software.com/appscan AppScan是一种综合型漏洞扫描工具&#xff0c;采用SaaS解决方案&#xff0c;它将所以测试功能整合到一个服务中&a…

样式的双向绑定的2种方式,实现样式交互效果

与样式标签实现双向绑定 通过布尔值来决定样式是出现还是消失 show代表着布尔值&#xff0c;show的初始值是false所以文本不会有高亮的效果&#xff0c;当用户点击了按钮&#xff0c;就会调用shows这个函数&#xff0c;并将show的相反值true赋值并覆盖给show,此时show的值为tru…

【秋招突围】2024届秋招笔试-小红书笔试题-第二套-三语言题解(Java/Cpp/Python)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系计划跟新各公司春秋招的笔试题 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f4e7; 清隆这边…

TalkingData数据统计,如何统计?

Ai文章推荐 1 作为程序员&#xff0c;开发用过最好用的AI工具有哪些&#xff1f; 2 Github Copilot正版的激活成功&#xff0c;终于可以chat了 3 idea,pycharm等的ai assistant已成功激活 4 新手如何拿捏 Github Copilot AI助手&#xff0c;帮助你提高写代码效率 5 Jetbrains的…

Java 中的重写(Override)与重载(Overload)

在Java编程语言中&#xff0c;“重写”&#xff08;Override&#xff09;和“重载”&#xff08;Overload&#xff09;是两个重要且常见的概念&#xff0c;它们虽然名字相近&#xff0c;但在功能、使用场景和实现方式上有着显著的区别。重写&#xff08;Override&#xff09;指…

诊断解决方案——CANdesc和MICROSAR

文章目录 一、CANdesc二、MICROSAR一、CANdesc canbeded是Vector汽车电子开发软件Nun Autosar标准的工具链之一。 canbeded是以源代码的形式提供的可重用的组件,包括CAN Driver,交互层(IL),网络管理(NM),传输层(TP),诊断层(CANdesc) , 通信测量和标定协议(CCP,XCP) 和 通信控…

TS中null和undefined特殊性

NUll&undefined null 与 undefined 也是变量类型&#xff0c;用于定义值为 null 或 undefined undefined 类型只包含一个值undefined&#xff0c;表示未定义&#xff08;即还未给出定义&#xff0c;以后可能会有定义&#xff09;。// undefined let Sakun09: undefined un…

嵌入式开发工具代码

文章目录 将字符串中的小写字母转换为大写循环队列&#xff08;Circular Buffer&#xff09;断言&#xff08;Assertion&#xff09;位域反转&#xff08;Bit Reversal&#xff09;固定点数运算&#xff08;Fixed-Point Arithmetic&#xff09;字节序转换&#xff08;Endiannes…

Rust创建基准测试bench

打开终端&#xff08;或命令提示符&#xff09;。 导航到父目录。 将 Rust 编译器切换到 nightly 版本&#xff1a; rustup default nightly 在该目录下运行 cargo init 命令来创建一个新的 Rust 项目&#xff0c;这将在当前目录下创建 Cargo.toml 和 src 目录&#xff1a; …

1430. 迷宫出口

一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由 ��nn 的格点组成,每个格点只有 22 种状态, 00 和 11,前者表示可以通行后者表示不能通行。 同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense…

404 页面代码

<template> <div class"container"><h1>404</h1> <div ><p class"text-center">当前页面无法访问,可能没有权限或已删除</p><p class"text-center"> 去别处看看吧</p> </div> <…

Internet Download Manager(IDM6.41)软件安装包下载及安装教程

Internet Download Manager有一个智能下载逻辑加速器&#xff0c;具有智能动态文件分割和安全的多部分下载技术&#xff0c;可以加速下载。与其他下载加速器和管理器不同&#xff0c;Internet下载管理器在下载开始之前对文件进行分段&#xff0c;而Internet下载管理器在下载过程…

Web前端引用图:深度解析与实践指南

Web前端引用图&#xff1a;深度解析与实践指南 在Web前端开发中&#xff0c;引用图&#xff08;Dependency Graph&#xff09;是一个至关重要的概念&#xff0c;它有助于我们更好地理解和管理项目的依赖关系。www.rmrbggkd.com本文将从四个方面、五个方面、六个方面和七个方面…

[Linux] TCP协议介绍(2): TCP协议的“三次握手“过程分析、超时重传机制介绍...

上一篇文章中, 已经介绍了TCP协议的数据格式, 简单分析了其与UDP协议 关于可靠性方面的差异 本篇文章, 介绍分析一下 使用TCP协议通信, 非常重要的一个过程: 三次握手 TCP的"三次握手" TCP协议是有连接的传输层协议, 即使用TCP协议通信, 是需要建立连接的 TCP协议…