SwiftUI 6.0(iOS 18)自定义容器值(Container Values)让容器布局渐入佳境(上)

在这里插入图片描述

概述

我们在之前多篇博文中已经介绍过 SwiftUI 6.0(iOS 18)新增的自定义容器布局机制。现在,如何利用它们对容器内容进行“探囊取物”和“聚沙成塔”,我们已然胸有成竹了。

在这里插入图片描述

然而,除了上述鬼工雷斧般的新技巧之外,SwiftUI 6.0 其实还提供了能更进一步增加容器布局自由度的新利器:自定义容器值(Container Values)。

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

  • 概述
  • 1. SwiftUI 6.0 中容器内容的遍历
  • 2. SwiftUI 6.0 之前的解决之道
  • 3. 什么是 SwiftUI 6.0 全新的自定义容器值(Container Values)
  • 总结

相信 SwiftUI 6.0 中全新的自定义容器值能够让容器布局更加“脱胎换骨”、灵动自由。

那还等什么呢?让我们马上开始新的容器大冒险吧!Let’s go!!!😉


1. SwiftUI 6.0 中容器内容的遍历

从 SwiftUI 6.0(iOS 18)开始,苹果为 ForEach 和 Group 视图增加了全新的构造器,使它们能够分别实现解析容器单个元素和“鸟瞰”容器整体内容的功能:

在这里插入图片描述
在这里插入图片描述

我们可以将它们看成是 SwiftUI 4.0 中自定义容器布局(Layout)的一个简化版本。

在之前的博客中,我们已经对其做过一些介绍,感兴趣的小伙伴们可以移步如下链接观赏精彩的内容:

  • SwiftUI 6.0(iOS 18)新容器视图修改器漫谈
  • SwiftUI 6.0(iOS 18)将 Sections 也考虑进自定义容器子视图布局(上)
  • SwiftUI 6.0(iOS 18)将 Sections 也考虑进自定义容器子视图布局(下)

下面,请允许我们先写一个非常简单的自定义容器 NiceListContainer 来小试拳脚一番:

struct NiceListContainer<Content: View>: View {@ViewBuilder var content: Contentvar body: some View {List {ForEach(subviews: content) { subview insubview}}}
}

如下代码所示,使用 NiceListContainer 容器很简单:

struct ContentView: View {var body: some View {NiceListContainer {Group {Text("Hello").foregroundStyle(.green)Text("World").foregroundStyle(.red)Text("大熊猫侯佩").foregroundStyle(.brown).font(.system(size: 55, weight: .black))Image(systemName: "globe.europe.africa").resizable().aspectRatio(contentMode: .fit).foregroundStyle(.orange.gradient)HStack {Text("战斗力")Slider(value: .constant(10)).padding()}.tint(.pink)}.font(.title.weight(.heavy))}}
}

我们在上面的演示代码中做了这样几件事:

  • 用 @ViewBuilder 语法将任意子元素放到了 NiceListContainer 容器中;
  • 使用 Group 将这些子元素聚为一组。这不会影响 NiceListContainer 主体中 ForEach(subviews:) 的解析,因为 ForEach 可以将组(Group)中的内容“解开”作为单独的容器元素来遍历;

编译并在 Xcode 预览里可以看到,我们成功的将 NiceListContainer 传入闭包中的每个子视图作为 List 中单个行呈现出来了:

在这里插入图片描述

现在假设我们要实现这样一种功能:在 NiceListContainer 容器中指定子视图的左侧(Leading)加上红色竖线以醒目用户。那么,我们怎么才能让 NiceListContainer 容器知晓哪些子视图需要醒目显示呢?

在下面的代码中,我们假想了这种行为。我们使用臆造的 highlightPrefix() 修改器来向 NiceListContainer 容器表明我们想在这些视图上增加醒目显示的意图:

struct ContentView: View {var body: some View {NiceListContainer {Group {Text("Hello").foregroundStyle(.green).highlightPrefix()Text("World").foregroundStyle(.red).highlightPrefix()Text("大熊猫侯佩").foregroundStyle(.brown).font(.system(size: 55, weight: .black))Image(systemName: "globe.europe.africa").resizable().aspectRatio(contentMode: .fit).foregroundStyle(.orange.gradient)HStack {Text("战斗力")Slider(value: .constant(10)).padding()}.tint(.pink).highlightPrefix()}.font(.title.weight(.heavy))}}
}

那么,我们应该怎样实现上面的 highlightPrefix() 修改器方法呢?大家可以先自己尝试一下。

2. SwiftUI 6.0 之前的解决之道

聪明的小伙伴们可能已经有了一些头绪:我们需要一种方法从容器中的子视图向容器传递消息。这有点像 SwiftUI 中的环境变量,但 @Environment 是从顶向下而不是从底部向上传递消息的。

在 SwiftUI 6.0 之前,我们可以使用 Preference 机制将与子视图绑定的 ID 向上层传递,然后在上层的容器视图中归拢这些 ID,并在这些 ID 对应的视图上应用特殊效果。


关于 Preference 机制的进一步介绍,大家可以到下面的链接中一探究竟:

  • 『番外篇六』SwiftUI 取得任意视图全局位置的三种方法
  • SwiftUI 在 App 中弹出全局消息横幅(下)

关于如何用动态探查技术在运行时获取指定视图的 id 或 tag 值,小伙伴们可以前往如下链接进一步观赏:

  • 『番外篇五』SwiftUI 进阶之如何动态获取任意视图的 tag 和 id 值

另一种办法是,我们可以用 SwiftUI 4.0 Layout 中的自定义布局值来将消息传递给父视图:

在这里插入图片描述

大致的实现如下代码所示:


struct Rotation: LayoutValueKey {static let defaultValue: Binding<Angle>? = nil
}struct ContentView: View {// ...@State var rotations: [Angle] = Array<Angle>(repeating: .zero, count: 16)var body: some View {WheelLayout(radius: radius, rotation: angle) {ForEach(0..<16) { idx inRoundedRectangle(cornerRadius: 8).fill(colors[idx%colors.count].opacity(0.7)).frame(width: 70, height: 70).overlay { Text("\(idx+1)") }.rotationEffect(rotations[idx])// 将自定义的 Rotation 初始值传递到 WheelLayout 中去.layoutValue(key: Rotation.self, value: $rotations[idx])}}// ...
}

在上面代码中,我们使用 layoutValue() 修改器将 Rotation 对应 LayoutValueKey 键的值向上传递给了 WheelLayout 容器。

在 WheelLayout 容器中,我们通过子视图的 LayoutValueKey 键语法糖计算了每个子视图适合的 Rotation 值:

struct WheelLayout: Layout {// ...func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {let angleStep = (Angle.degrees(360).radians / Double(subviews.count))for (index, subview) in subviews.enumerated() {let angle = angleStep * CGFloat(index) + rotation.radians// ...DispatchQueue.main.async {// 计算每个子视图对应的旋转值subview[Rotation.self]?.wrappedValue = .radians(angle)}}}
}

虽然上面这些方法可行,但总觉得有些莫可名状。有没有更简单、更优雅的方法呢?

答案自然是:确定、一定、以及肯定!

3. 什么是 SwiftUI 6.0 全新的自定义容器值(Container Values)

从 SwiftUI 6.0 开始,苹果为定制容器布局新增了自定义容器值(Container Values)的概念。

在这里插入图片描述

简单来说,我们可以使用自定义容器值将所需的状态值附加到容器中指定的子视图上,然后传递到容器的解析和再组合中。

利用 SwiftUI 6.0 中全新的 @Entry 宏,我们还可以进一步简化 Container Values 的定义。

在这里插入图片描述


细心的小伙伴们可能发现了,在上图中的 Entry 宏貌似从 iOS 13(SwiftUI 1.0)就开始支持了,如果不是苹果“犯浑”的话,原因可能是苹果决定把这个特性大幅度向前兼容吧。

更多 @Entry 宏的介绍,请小伙伴们移步如下链接观赏进一步的内容:

  • SwiftUI 6.0(Xcode 16)全新 @Entry 和 @Previewable 宏让开发妙趣横生

在下一篇博文中,我们就来看看如何在 SwiftUI 6.0 中优雅的使用 Container Values 吧。

总结

在本篇博文中,我们介绍了如何用 SwiftUi 6.0 全新的自定义容器机制解析容器子元素,并初步介绍了何为 SwiftUI 6.0 全新的自定义容器值(Container Values)。

感谢观赏,再会啦!😎

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

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

相关文章

finereport 数据下钻

目标&#xff1a;点击某块汇总的单元格&#xff0c;然后直接在原表的位置下钻到明细表&#xff0c;且不会影响整个大屏的结构&#xff0c;同时又支持明细表再回退到汇总表的功能 1、新建tab组件 1、新建决策报表 将 body 的布局方式改为「绝对布局」 2、将 Tab 块拖入 body…

小白都来用这款AI绘画神器,IDEOGRAM2.0,轻松画出高质量图片

大家好&#xff01;我是宇航&#xff0c;一位喜欢AI绘画的10年技术专家&#xff0c;专注于输出AI绘画与视频内容 今天给大家介绍一款绝对的生图神器——Ideogram2.0! 不论你是AI小白&#xff0c;手残党还是资深玩家&#xff0c;无论你是做网页设计&#xff0c;电商&#xff0c…

【Python爬虫实战】正则:从基础字符匹配到复杂文本处理的全面指南

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、正则表达式 &#xff08;一&#xff09;正则表达式的基本作用 &#xf…

The Android SDK location cannot be at the filesystem root

win11&#xff0c; 安装启动完Android Studio后&#xff0c;一直显示 The Android SDK location cannot be at the filesystem root因此需要下载SDK包&#xff0c;必须开启代理。 开启代理后&#xff0c;在System下开启自动检测代理&#xff0c;如图 重启Android Studio&a…

【微信小程序_11_全局配置】

摘要:本文介绍了微信小程序全局配置文件 app.json 中的常用配置项,重点阐述了 window 节点的各项配置,包括导航栏标题文字、背景色、标题颜色,窗口背景色、下拉刷新样式以及上拉触底距离等。通过这些配置可实现小程序窗口外观的个性化设置,提升用户体验。 微信小程序_11_全…

C语言 | Leetcode C语言题解之第462题最小操作次数使数组元素相等II

题目&#xff1a; 题解&#xff1a; static inline void swap(int *a, int *b) {int c *a;*a *b;*b c; }static inline int partition(int *nums, int left, int right) {int x nums[right], i left - 1;for (int j left; j < right; j) {if (nums[j] < x) {swap(…

树莓派应用--AI项目实战篇来啦-5.OpenCV绘画函数的使用

1. 介绍 OpenCV作为一款功能强大的计算机视觉库&#xff0c;被广泛地应用于图像处理和计算机视觉领域。 除了在机器视觉和人工智能领域有者广泛的应用&#xff0c;OpenCV 还能够媲美艺术家的创造力&#xff0c;通过其强大的绘图函数&#xff0c;绘制出令人叹为观止的艺术画作。…

flask项目框架搭建

目录结构 blueprints python包&#xff0c;蓝图文件&#xff0c;相当于路由组的概念,方便模块化开发 例如auth.py文件 from flask import Blueprint, render_templatebp Blueprint("auth", __name__, url_prefix"/auth")bp.route("/login") d…

Python数据可视化常用工具,值得收藏!!!

我们了解了如何使用 Pandas 进行简单的绘图,使用 Pandas 自带的绘图功能能够快速地生成一些基本的图表,例如折线图、柱状图等.但为了实现更复杂或专业的可视化效果,我们通常还需要借助更为强大的绘图库——Matplotlib. 本篇文章将详细介绍如何结合 Matplotlib 和 Pandas 实现数…

Redis-缓存一致性

缓存双写一致性 更新策略探讨 面试题 缓存设计要求 缓存分类&#xff1a; 只读缓存&#xff1a;&#xff08;脚本批量写入&#xff0c;canal 等&#xff09;读写缓存 同步直写&#xff1a;vip数据等即时数据异步缓写&#xff1a;允许延时&#xff08;仓库&#xff0c;物流&a…

C++: AVL树的实现

一.AVL树的旋转 AVL树是平衡搜索二叉树的一种。 平衡因子&#xff1a;节点右树的高度减左树的高度&#xff0c;AVL树规定平衡因子的绝对值小于2。若不在这个范围内&#xff0c;说明该树不平衡。 AVL树节点&#xff1a; struct AVLTreeNode {AVLTreeNode(const T& data …

数据结构--堆的深度解析

目录 引言 一、基本概念 1.1堆的概念 1.2堆的存储结构 1.3堆的特点 二、 堆的基本操作 2.1初始化 2.2创建堆 2.3插入元素 2.4删除元素 2.5堆化操作 2.6堆的判空 2.7获取堆顶元素 三、堆的常见应用 1. 优先队列 2. 堆排序 3. Top-k 问题 4. 图论中的应用 四…

rom定制系列------小米5x_miui12安卓11定制固件界面预览 小米5x第三方固件

&#x1f49d;&#x1f49d;&#x1f49d;此固件来源于客户卡刷固件定制。客户需要修改为线刷。并且修改账号锁功能。 可以让客户使用官方平台批量进行刷写。方便操作。 定制机型以及功能预览&#x1f49d;&#x1f49d;&#x1f49d; 小米5x版本miui12.5.8安卓11固件。此机型…

MySQL 连接

使用MySQL二进制方式连接 使用MySQL二进制方式进入到MySQL命令提示符下来连接MySQL数据库。 实例 以下是从命令行中连接MySQL服务器的简单实例&#xff1a; [roothost]# mysql -u root -p Enter password:******在登录成功后会出现 mysql> 命令提示窗口&#xff0c;你可以在…

Java 文件拷贝

1.小文件拷贝 实例代码&#xff1a; 上面程序运行的图示&#xff1a; 弊端&#xff1a;一次读一个字节&#xff0c;效率太慢。所以需要一次读取多个字节。 2.大文件拷贝 结果&#xff1a;

UE5运行时动态加载场景角色动画任意搭配-全流程代码(四)

UE5运行时动态加载场景、角色、角色动画、相机动画任意搭配,Android、iOS也可以跑,横竖屏兼容,手机竖屏: 1、场景切换UWorld处理 在通过OpenLevel进行场景切换的时候,UWorld会发生变化,需要我们获取正确的UWorld。 1、在GameInstance监听Level加载 void UMyGameInsta…

数据结构——复杂度

目录 数据结构前言 数据结构 算法 算法效率 时间复杂度 大O的渐进表示法 示例1 示例2 示例3 示例4 示例5 示例6 示例7 空间复杂度 示例1 示例2 示例3 示例4 常见复杂度对比 旋转数组 优化1 优化2 这一篇文章我们就开始数据结构知识的学习&#xff01; 数据…

stm32单片机个人学习笔记10(TIM编码器接口)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

Jetpack-Room

Room是Android Jetpack中的一个组件&#xff0c;它提供了一个抽象层&#xff0c;帮助开发者在本地数据库&#xff08;如SQLite&#xff09;上进行持久化数据存储。Room通过简化数据库操作&#xff0c;使得数据管理变得更加容易和高效。 Room重要的概念 实体&#xff08;Entit…

Python即时获取上证指数信息并发送邮件到指定邮箱

最近股市振荡&#xff0c;股民会时不时查看一下上证指数信息&#xff0c;但是繁忙的工作却时不是让人忘记了&#xff0c;错过了投资的最佳时机&#xff0c;这时就可以通过Python来爬取网站上的上证指数&#xff0c;并发送到指定的邮箱&#xff0c;这样就不用上指定网页也以获取…