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

news2025/1/18 7:32:20

在这里插入图片描述

概述

我们在之前多篇博文中已经介绍过 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: Content
    
    var body: some View {
        List {
            ForEach(subviews: content) { subview in
                subview
            }
        }
    }
}

如下代码所示,使用 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 in
                RoundedRectangle(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.coloradmin.cn/o/2214160.html

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

相关文章

STM32_实验1_建立新工程

1、使用STM32CubeIDE建立一个新工程 1.1选择时钟源为外部晶振时钟。 1.2选择调试方式为 serial wire&#xff08;串行线&#xff09;。 1.3配置时钟树. 1.4选择以 c 和 h 文件型式管理工程文件。 1.5生成 hex 可执行文件。&#xff08;完成后点击锤子&#xff09; 2.串口输出调…

鸿蒙进入“无人区”:该如何闯关?

按照华为方面的说法&#xff0c;“打造鸿蒙操作系统是三大战役&#xff0c;目前已经完成了底座和体验两大战役&#xff0c;第三大战役则是生态。”生态固然重要&#xff0c;但要让鸿蒙与当今世界主流操作系统抗衡&#xff0c;乃至成为新一代操作系统中的翘楚&#xff0c;其实还…

每个程序员都应该了解的硬件知识

作者:shizhaoyang 在追求高效代码的路上,我们不可避免地会遇到代码的性能瓶颈。为了了解、解释一段代码为什么低效,并尝试改进低效的代码,我们总是要了解硬件的工作原理。于是,我们可能会尝试搜索有关某个架构的介绍、一些优化指南或者阅读一些计算机科学的教科书(如:计…

卡码网KamaCoder 94. 城市间货物运输 I

题目来源&#xff1a;94. 城市间货物运输 I C题解1&#xff08;来源代码随想录&#xff09;&#xff1a;Bellman_ford 本题是经典的带负权值的单源最短路问题&#xff0c;此时就轮到Bellman_ford登场了。Bellman_ford算法的核心思想是 对所有边进行松弛n-1次操作&#xff08;…

【 ACM独立出版】第二届通信网络与机器学习国际学术会议(CNML 2024,10月25-27)

官方信息 会议官网&#xff1a;www.cn-ml.org The 2nd International Conference on Communication Networks and Machine Learningwww.cn-ml.org 时间地点&#xff1a;2024年10月25-27日 | 中国-河南-郑州 截稿时间&#xff1a;2024年10日19日 &#xff08;多轮截稿&#x…

51单片机的晾衣架控制系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器光照传感器步进电机按键、LED、蜂鸣器等模块构成。适用于智能晾衣架等相似项目。 可实现功能: 1、LCD1602实时显示温湿度、光照强度和手动/自动信息 2、温湿度传感器DHT11采集温湿度信息 3、光照传感…

【数据结构与算法初阶】顺序表(上)

什么语言实现不重要&#xff0c;理解了思路就行&#xff0c;本篇使用C语言实现 一.顺序表含义(重要) 首先&#xff0c;顺序表属于线性表中的一中&#xff0c;线性表可以用多种方式实现&#xff0c;顺序表只是其中的一种 --------- 线性表是啥呢&#xff0c;通俗的说&#xff0…

lego-loam imageProjection.cpp源码注释(一)

一、主函数 int main(int argc, char** argv){ros::init(argc, argv, "lego_loam");ImageProjection IP;ROS_INFO("\033[1;32m---->\033[0m Image Projection Started.");ros::spin();return 0; }主函数很简单&#xff0c;常规ros初始化ros::init&…

程序员35岁丢了工作,应该怎么活?

35岁对很多程序员来说是个敏感的年龄段。在这个阶段&#xff0c;许多程序员已经有了丰富的工作经验和较高的薪水&#xff0c;但同时也面临着职场上不可忽视的年龄压力。尤其在一些技术密集型的公司&#xff0c;35岁之后的程序员可能被认为“年纪大了”&#xff0c;不再是招聘市…

【C语言】动态内存管理及相关笔试题

文章目录 一、为什么有动态内存分配二、malloc和free1.malloc函数的使用2.free函数的使用 三、calloc和realloc1.calloc函数的使用2.realloc函数的使用 四、常见动态内存分配的错误五、动态内存经典笔试题题1题2题3 六、总结C/C中程序内存区域划分 一、为什么有动态内存分配 我…

Struct Streaming

spark进行实时数据流计算时有两个工具 Spark Streaming:编写rdd代码处理数据流,可以解决非结构化的流式数据 Structured Streaming:编写df代码处理数据流,可以解决结构化和半结构化的流式数据 实时计算 实时计算&#xff0c;通常也称为“实时流计算”、“流式计算” 流数据处…

面腾讯后台开发,二面挂掉了,,,

随着各厂秋招的开启&#xff0c;收到面试邀请的同学也越来越多。在当年和我一起找实习的同学里面&#xff0c;有实力较强的同学收到了腾讯后台开发的校招面试邀请。但面试不止是实力的竞争&#xff0c;也有很重要的运气的因素。 虽然我的同学在腾讯后台开发的二面中挂掉了&…

Mybatis中的映射文件编写原则

先来回顾一下&#xff0c;在Java项目中如何使用Mybatis执行SQL语句&#xff1a; 添加依赖&#xff1a;在项目中添加 MyBatis 和数据库驱动的依赖。配置 MyBatis&#xff1a;创建 MyBatis 的配置文件&#xff0c;配置数据源和 Mapper 映射。创建 POJO 类&#xff1a;定义与数据…

拒绝飞单,微信监控轻松搞定!

微信作为广泛使用的社交工具&#xff0c;其安全性和监控问题受到了广泛关注。对于企业来说&#xff0c;确保客户资源的安全和防止员工“飞单”是重要的管理挑战。以下是一些有效的方法和工具&#xff0c;可以帮助企业提高微信的安全性&#xff0c;防止飞单&#xff0c;从而保护…

LLM - 配置 ModelScope SWIFT 测试 Qwen2-VL 模型推理(Infer) 教程 (1)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142827217 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 SWIFT …

QT文件操作【记事本】

mainwindow.h核心函数 QFileDialog::getOpenFileName()QFileDialog::getSaveFileName() #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include<QFileDialog> #include<QMessageBox> #include<QDebug> #include<QFile> #…

Apache Kafka的生态

Kafka 生态系统 微信公众号&#xff1a;阿俊的学习记录空间 小红书&#xff1a;ArnoZhang wordpress&#xff1a;arnozhang1994 博客园&#xff1a;arnozhang CSDN&#xff1a;ArnoZhang1994 以下是与 Kafka 集成的工具列表&#xff0c;涵盖了不同领域的工具和扩展。这些…

Jmeter如何进行多服务器远程测试?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 JMeter是Apache软件基金会的开源项目&#xff0c;主要来做功能和性能测试&#xff0c;用Java编写。 我们一般都会用JMeter在本地进行测试&#xff0c;但是受到单…

鱼跃医疗荣获深交所信息披露工作“A”级评价

2024年10月11日&#xff0c;深圳证券交易所&#xff08;以下简称“深交所”&#xff09;发布了《关于深市上市公司2023-2024年度信息披露评价结果的通报》&#xff0c;鱼跃医疗&#xff08;002223.SZ&#xff09;凭借在信息披露质量、投资者关系管理等各方面的优异表现&#xf…

总结拓展十四:批次管理(1)

1、批次的概念 批次是指生产或采购过程中&#xff0c;为了区分不同供应商之间相同产品间的微小区别而进行的管理方式。它通常用于确保产品质量的一致性和可追溯性。批次的概念可以应用于多个领域&#xff0c;包括生产、采购、物流、销售等。 2、批次管理的概念 批次管理是指对…