SwiftUI 6.0(iOS 18)新增的网格渐变色 MeshGradient 解惑

在这里插入图片描述

概述

在 SwiftUI 中,我们可以借助渐变色(Gradient)来实现更加灵动多彩的着色效果。从 SwiftUI 6.0 开始,苹果增加了全新的网格渐变色让我们对其有了更自由的定制度。

在这里插入图片描述

因为 gif 格式图片自身的显示能力有限,所以上面的动图无法传神的还原实际的美妙效果。强烈建议大家在模拟器或真机上运行本文中的示例代码。

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

  • 概述
  • 1. 渐变色的前世今生
  • 2. 动画加持,美轮美奂
  • 3. 综合运用
  • 总结

闲言少叙,让我们马上进入渐变色的世界吧!

Let‘s dive in!!!😉


1. 渐变色的前世今生

在 SwiftUI 中小伙伴们时常会用渐变色(或称为阶梯色)来装扮我们的界面。

在这里插入图片描述

在 SwiftUI 1.0(iOS 13)中有 3 种渐变色类型,它们分别是:线性渐变色 LinearGradient、辐射渐变色 RadialGradient、以及角度渐变色 AngularGradient。

在这里插入图片描述

关于使用它们对进行任意视图裁剪的进一步介绍,请小伙伴们移步如下链接观赏精彩的内容:

  • SwiftUI用Gradient颜色裁剪任意视图

而在 SwiftUI 3.0(iOS 15)中,苹果又添加了一款椭圆渐变色 EllipticalGradient:

在这里插入图片描述

为了能够更加轻松的使用单一颜色的渐变色,苹果从 SwiftUI 4.0(iOS 16)开始干脆将其直接“融入”到 Color 的实例中去了:

在这里插入图片描述

我们可以这样使用它:

Text("Hello Panda")
	.foregroundStyle(.red.gradient)

在 WWDC 24 中,苹果再接再厉为 SwiftUI 6.0(iOS 18)添加了全新渐变色风格:网格渐变色(MeshGradient ):

在这里插入图片描述

别被它的名字所吓到,其实它只是用纵横交错的方格来进一步细粒度控制颜色渐变的自由度,仅此而已。

使用网格渐变色很简单,我们只需创建一个 MeshGradient 实例即可:

MeshGradient(
        width: 2,
        height: 2,
        points: [.init(x: 0, y: 0),.init(x: 1, y: 0), .init(x: 0, y: 1), .init(x: 1, y: 1)],
        colors: [.red, .green, .blue, .yellow]
    )

如上代码所示:我们创建了一个 2 x 2 网格渐变色,并将其左上角、右上角、左下角、右下角的颜色依次设置为红色、绿色、蓝色以及黄色:

在这里插入图片描述

现在我们“静如处子”的网格渐变色貌似略显“呆滞”。别急,通过适当地调整其内部各个网格边框的基准点(或者颜色),我们可以让它行云流水般的“动如脱兔”。

2. 动画加持,美轮美奂

上面说过,要想动画网格渐变色很简单。我们只需使用若干状态来实时的描述 MeshGradient 中每个网格边框的相对位置以及网格内的颜色即可。

首先,我们创建一个 positions 数组来表示每个网格的边框,注意这是一个 3 x 3 网格:

@State var positions: [SIMD2<Float>] = [
        .init(x: 0, y: 0), .init(x: 0.2, y: 0), .init(x: 1, y: 0),
        .init(x: 0, y: 0.7), .init(x: 0.1, y: 0.5), .init(x: 1, y: 0.2),
        .init(x: 0, y: 1), .init(x: 0.9, y: 1), .init(x: 1, y: 1)
    ]

接下来,我们利用定时器连续调整 positions 里所有非顶角网格边框的相对位置。排除顶角网格的原因是:我们不想让整个网格渐变色在顶点被裁剪:

在这里插入图片描述

具体实现代码如下所示:

let timer = Timer.publish(every: 1/9, on: .current, in: .common).autoconnect()
    
let colors: [Color] = [
    .purple, .red, .yellow,
    .blue, .green, .orange,
    .indigo, .teal, .cyan
]

func randomizePosition(
    currentPosition: SIMD2<Float>,
    xRange: (min: Float, max: Float),
    yRange: (min: Float, max: Float)
) -> SIMD2<Float> {
    let updateDistance: Float = 0.01

    let newX = if Bool.random() {
        min(currentPosition.x + updateDistance, xRange.max)
    } else {
        max(currentPosition.x - updateDistance, xRange.min)
    }

    let newY = if Bool.random() {
        min(currentPosition.y + updateDistance, yRange.max)
    } else {
        max(currentPosition.y - updateDistance, yRange.min)
    }

    return .init(x: newX, y: newY)
}

MeshGradient(
        width: 3,
        height: 3,
        points: positions,
        colors: colors
    )
    .animation(.bouncy, value: positions)
    .onReceive(timer, perform: { _ in
        positions[1] = randomizePosition(
            currentPosition: positions[1],
            xRange: (min: 0.2, max: 0.9),
            yRange: (min: 0, max: 0)
        )
        
        positions[3] = randomizePosition(
            currentPosition: positions[3],
            xRange: (min: 0, max: 0),
            yRange: (min: 0.2, max: 0.8)
        )
        
        positions[4] = randomizePosition(
            currentPosition: positions[4],
            xRange: (min: 0.3, max: 0.8),
            yRange: (min: 0.3, max: 0.8)
        )
        
        positions[5] = randomizePosition(
            currentPosition: positions[5],
            xRange: (min: 1, max: 1),
            yRange: (min: 0.1, max: 0.9)
        )
        
        positions[7] = randomizePosition(
            currentPosition: positions[7],
            xRange: (min: 0.1, max: 0.9),
            yRange: (min: 1, max: 1)
        )
    })
    .animation(.bouncy, value: positions)
    .ignoresSafeArea()

编译并在 Xcode 预览中运行一见分晓:

在这里插入图片描述

再次重申:上面动图“颗粒感”很强是因为 gif 图片本身对颜色限制(最多显示 256 种颜色)的原因,实际效果会相当丝滑顺畅。

现在,我们不但可以恣意描绘静态渐变色,利用些许动画我们还可以让它活灵活现的呈现“秾姿故薰欲醉眼,芳信暗传尝苦心”之意境,棒棒哒!💯


想要系统学习最新 Swift 语言如何美美哒的进行苹果开发的小伙伴们,可以到我的《Swift语言开发精讲》专栏来逛一逛哦:

在这里插入图片描述

  • Swift 语言开发精讲

3. 综合运用

下面是一个将网格渐变色溶入到我们实际应用中的演示代码。在代码中我们做了这样几件事:

  • 用不同状态控制不同的动画效果
  • 使用 mask 将网格渐变色嵌入到文本视图中
  • 扩展 View 以实现更简洁的视图方法

全部源代码在此:

import SwiftUI

extension View {
    @ViewBuilder
    func scaleEffect(_ ratio: CGFloat) -> some View {
        scaleEffect(x: ratio, y: ratio)
    }
}

struct ContentView: View {
    
    @State var bgAnimStart = false
    @State var shadowAnimStart = false
    
    @State var positions: [SIMD2<Float>] = [
        .init(x: 0, y: 0), .init(x: 0.2, y: 0), .init(x: 1, y: 0),
        .init(x: 0, y: 0.7), .init(x: 0.1, y: 0.5), .init(x: 1, y: 0.2),
        .init(x: 0, y: 1), .init(x: 0.9, y: 1), .init(x: 1, y: 1)
    ]

    let timer = Timer.publish(every: 1/9, on: .current, in: .common).autoconnect()
    
    let colors1: [Color] = [
        .purple, .red, .yellow,
        .blue, .green, .orange,
        .indigo, .teal, .cyan
    ]
    
    let colors2: [Color] = [
        .black, .red, .blue,
        .black, .teal, .blue,
        .blue, .red, .black
    ]

    func randomizePosition(
        currentPosition: SIMD2<Float>,
        xRange: (min: Float, max: Float),
        yRange: (min: Float, max: Float)
    ) -> SIMD2<Float> {
        let updateDistance: Float = 0.01

        let newX = if Bool.random() {
            min(currentPosition.x + updateDistance, xRange.max)
        } else {
            max(currentPosition.x - updateDistance, xRange.min)
        }

        let newY = if Bool.random() {
            min(currentPosition.y + updateDistance, yRange.max)
        } else {
            max(currentPosition.y - updateDistance, yRange.min)
        }

        return .init(x: newX, y: newY)
    }
    
    func createMeshGradientView(_ colors: [Color]) -> some View {
        MeshGradient(
            width: 3,
            height: 3,
            points: positions,
            colors: colors
        )
        .animation(.bouncy, value: positions)
        .onReceive(timer, perform: { _ in
            positions[1] = randomizePosition(
                currentPosition: positions[1],
                xRange: (min: 0.2, max: 0.9),
                yRange: (min: 0, max: 0)
            )
            
            positions[3] = randomizePosition(
                currentPosition: positions[3],
                xRange: (min: 0, max: 0),
                yRange: (min: 0.2, max: 0.8)
            )
            
            positions[4] = randomizePosition(
                currentPosition: positions[4],
                xRange: (min: 0.3, max: 0.8),
                yRange: (min: 0.3, max: 0.8)
            )
            
            positions[5] = randomizePosition(
                currentPosition: positions[5],
                xRange: (min: 1, max: 1),
                yRange: (min: 0.1, max: 0.9)
            )
            
            positions[7] = randomizePosition(
                currentPosition: positions[7],
                xRange: (min: 0.1, max: 0.9),
                yRange: (min: 1, max: 1)
            )
        })
    }
    
    let text = Text("Hello Panda")
        .font(.system(size: 108, weight: .heavy, design: .rounded))
        .foregroundStyle(.red.gradient)

    var body: some View {
         
        NavigationStack {
            ZStack {
                
                createMeshGradientView(colors1)
                    //.blur(radius: 30.0)
                    .opacity(0.8)
                
                text
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .opacity(0.01)
                    .background {
                        createMeshGradientView(colors2)
                            .mask {
                                text
                                    .scaleEffect(bgAnimStart ? 1.1 : 1.0)
                                    .rotationEffect(.degrees(bgAnimStart ? -10 : 0))
                            }
                            .shadow(color: shadowAnimStart ? .green : .black, radius: 10)
                    }
                
                
            }
            .ignoresSafeArea()
            .navigationTitle("Mesh Gradient 演示")
            .toolbar {
                Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
                    .foregroundStyle(.primary.secondary)
                    .font(.headline)
            }
        }
        .task {
            withAnimation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true)) {
                shadowAnimStart = true
            }
            
            withAnimation(.snappy(duration: 0.66, extraBounce: 15.0).repeatForever(autoreverses: true)) {
                bgAnimStart = true
            }
        }
    }
}

#Preview {
    ContentView()
}

总结

在本篇博文中,我们讨论了 SwiftUI 6.0(iOS 18)中全新网格渐变色 MeshGradient 的使用,并随后介绍如何利用酷炫的动画升华它的动态效果。

感谢观看,再会啦!😎

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【自动驾驶汽车通讯协议】GMSL通信技术以及加串器(Serializer)解串器(Deserializer)介绍

文章目录 0. 前言1. GMSL技术概述2. 为什么需要SerDes&#xff1f;3. GMSL技术特点4.自动驾驶汽车中的应用5. 结论 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff0c;虽然参考了他人的宝贵见解及成果&#xff0c;但是内容可能存在不准…

六西格玛黑带项目:TBX-02无人机飞行稳定性提升——张驰咨询

一、项目背景与问题定义 TBX-02是该公司最新发布的消费级无人机&#xff0c;面向摄影爱好者和户外探险者。产品上市后&#xff0c;通过客户反馈和实际测试数据发现&#xff0c;该无人机在复杂飞行环境中&#xff0c;如强风或快速移动时&#xff0c;存在明显的飞行抖动和稳定性…

RabbitMQ初识

目录 Kafka RocketMQ RabbitMQ MQ界面(它使用的端口号5672&#xff0c;界面是15672&#xff09; 如何添加一个虚拟机&#xff0c;点击右侧 Topics&#xff08;通配符模式&#xff09; 发布确认机制 持久性(可靠性保证的机制之一) JDK17,Linux服务器Ubuntu 什么是MQ 实…

前端开发笔记--html 黑马程序员2

文章目录 前端常用标签一、标题标签二、段落标签和换行标签和水平线标签三、文本格式化标签![请添加图片描述](https://i-blog.csdnimg.cn/direct/87583fa23fe04229b016912051f3fc45.png)四、盒子标签五、图像标签六、连接标签七、注释和特殊字符 八、表格标签的基本使用九、列…

48 Redis

48 Redis 前言 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务。是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 redis会周期性的把更新的数据写入磁盘或者把修改操…

数据结构-5.6.二叉树的先,中,后序遍历

一.遍历&#xff1a; 二.二叉树的遍历&#xff1a;利用了递归操作 1.简介&#xff1a; 二叉树的先序遍历&#xff0c;中序遍历&#xff0c;后序遍历都是以根结点遍历顺序为准的&#xff0c;如先序遍历就先遍历根结点 2.实例&#xff1a; 例一&#xff1a; 例二&#xff1a; …

HarmonyOS NEXT应用开发实战(二、封装比UniApp和小程序更简单好用的网络库)

网络访问接口&#xff0c;使用频次最高。之前习惯了uniapp下的网络接口风格&#xff0c;使用起来贼简单方便。转战到鸿蒙上后&#xff0c;原始网络接口写着真累啊&#xff01;目标让鸿蒙上网络接口使用&#xff0c;简单程度比肩uniapp&#xff0c;比Axios更轻量级。源码量也不多…

JUC并发编程进阶1:线程基础知识复习

1 从start一个线程说起 在 Java 中&#xff0c;Thread 类是用于创建和管理线程的核心类。通过调用 Thread 类的 start() 方法&#xff0c;可以启动一个新的线程&#xff0c;并执行线程的 run() 方法。下面我们来详细分析一下 start() 方法的实现。 1.1 代码示例 首先&#x…

前端开发笔记--html 黑马程序员1

文章目录 前端开发工具--VsCode前端开发基础语法VsCode优秀插件Chinese --中文插件Auto Rename Tag --自动重命名插件open in browserOpen in Default BrowserOpen in Other Browser Live Server -- 实时预览 前端开发工具–VsCode 轻量级与快速启动 快速加载&#xff1a;VSCo…

大数据毕业设计选题推荐-音乐数据分析系统-音乐推荐系统-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

ansible自动化运维,一些基础命令、更方便掌握ansible。

1.先准备三台机子&#xff0c;一台ansible服务端、和两台客户端&#xff0c;配置客户端主机名、cinder和compute。 192.168.10.202ansible客户端192.168.10.56cinder客户端192.168.10.55compute客户端 2.下载ansible&#xff08;客户端&#xff09;,准备repo文件。 #编写文件…

“网络安全等级保护测评入门:基础概念与重要性“

网络安全等级保护测评&#xff08;简称“等保测评”&#xff09;是依据国家网络安全等级保护制度&#xff0c;对信息系统安全等级进行评估和评定的过程。它是提高信息系统安全性、保障信息安全的重要手段。以下是关于等保测评的基础概念与重要性的详细解读&#xff1a; 一、等…

在docker的容器内如何查看Ubuntu系统版本

文章目录 写在前面一、问题描述二、解决方法参考链接 写在前面 自己的测试环境&#xff1a; docker 一、问题描述 由于 lsb_release -a 只能查看自己电脑&#xff08;宿主机&#xff09;的系统版本&#xff0c;如果在docker的容器内又应该如何查看Ubuntu系统版本呢&#xff…

IDEA运行Java程序时出错。提示:命令行过长。通过 JAR 清单或通过类路径文件缩短命令行,然后重新运行。

文章目录 一、遇到问题二、分析问题三、解决办法 一、遇到问题 运行 OpenCVUtils.test 时出错。命令行过长。 通过 JAR 清单或通过类路径文件缩短命令行&#xff0c;然后重新运行。二、分析问题 IDEA提示很明显了。 三、解决办法 运行——>编辑配置 运行/调试配置——&g…

欧科云链研究院深掘链上数据:洞察未来Web3的隐秘价值

目前链上数据正处于迈向下一个爆发的重要时刻。 随着Web3行业发展&#xff0c;公链数量呈现爆发式的增长&#xff0c;链上积聚的财富效应&#xff0c;特别是由行业热点话题引领的链上交互行为爆发式增长带来了巨量的链上数据&#xff0c;这些数据构筑了一个行为透明但与物理世…

模型 知识诅咒

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。知者难悟无知惑。 1 知识诅咒案例 1.1 会议室的误解 李经理是一家科技公司的产品经理&#xff0c;他负责领导一个新产品的开发项目。项目团队由不同背景和经验的成员组成&#xff0c;包括新入职的员…

kibana 删除es指定数据,不是删除索引

1 查询条件查询出满足条件的数据 GET /order_header_idx_202410/_search {"from":0,"size":10,"query":{"bool":{"filter":[{"term":{"oh_tenantId":{"value":"0211000001",&…

GitHub简介与安装使用入门教程

1、Git与GitHub的简介 Git是目前世界上最先进的分布式控制系统&#xff0c;它允许开发者跟踪和管理源代码的改动历史记录等&#xff0c;可以将你的代码恢复到某一个版本&#xff0c;支持多人协作开发。它的核心功能包括版本控制、分支管理、合并和冲突解决等&#xff0c;其操作…

JavaWeb概述及HTML | JavaWeb系列教程 | 第一期 | 前端

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 今天毛毛张分享的是JavaWeb系列笔记第一期&#xff1a;JavaWeb概述及HTML语法 特别说明&#xff1a;本系列教程的整理全部来源于尚硅谷的JavaWeb课程笔记&#xff0c…

基于Python Django的在线考试管理系统

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…