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

news2024/11/26 9:50:51

在这里插入图片描述

概述

在 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.coloradmin.cn/o/2201986.html

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

相关文章

springboot网站开发-mysql数据库字段varchar类型存储汉字的长度关系

springboot网站开发-mysql数据库字段varchar类型存储汉字的长度关系&#xff01; 如果你的数据表是utf-8编码&#xff0c;并且采用的是mysql数据库。设计自己的业务数据。那么&#xff0c;如果你采用是varchar类型的字段格式&#xff0c;一个汉字就是一个字节。 如图所示&#…

zookeeper API使用——znode的CRUD

这应该是目录 一、创建连接1.1构造函数1.2创建连接(封装) 二、节点操作2.1创建节点create语法代码 2.2获取节点数据getData语法代码 2.3获取子节点getChildren语法代码 2.4修改节点的值setData语法代码 2.5删除delete语法代码 以下的代码只能作为基础的语法使用&#xff0c;为了…

带隙基准Bandgap电路学习(二)

一、高低温性能测试 设置仿真环境温度为-40℃ &#xff0c;观察电路管子工作状态是否正确 环路相位裕度为53.1853 输出噪声曲线&#xff1a; 电源抑制PSR曲线&#xff1a; 设置仿真环境温度为85℃ &#xff0c;观察电路管子工作状态是否正确 环路相位裕度为53.7297 输出噪…

Windows 11 24H2版本有哪些新功能_Windows 11 24H2十四大新功能介绍

距离上次发布的23H2版本已经过去了一年时间&#xff0c;现在&#xff0c;Win 11的24H2版本终于等到了&#xff0c;微软已经全面公开发布Win11 24H2版本&#xff0c;版本号为26100.1742&#xff0c;此次官宣的版本包括了消费者版、商业版、LTSC 2024版等&#xff0c;各种语言版本…

旅游景区小程序|提供多元化服务,打造一站式旅游体验

今年国庆黄金周&#xff0c;各地精心筹备&#xff0c;打造了丰富多彩的文化旅游盛宴&#xff0c;吸引了来自四面八方的游客。凭借各地的自然风光、丰富的人文景观以及精彩纷呈的旅游活动&#xff0c;吸引众多游客。此次国庆旅游市场的火爆&#xff0c;不仅为当地带来了可观的经…

C语言预处理详解(上)(30)

文章目录 前言一、预定义符号二、#define定义标识符三、#define定义宏四、#define的替换规则五、带有副作用的宏六、宏和函数的对比七、#undef的作用八、# 和#的作用##的作用 总结 前言 C语言的入门学习差不多要到尾声了&#xff0c;感觉如何呢~   前文说编译的第一步就是预编…

ax1.twinx()函数介绍

目录 ax1.twinx()实战 ax1.twinx() ax1.twinx() 是 Matplotlib 中用于创建共享 x 轴的双 y 轴的函数。在 Matplotlib 中&#xff0c;当我们希望在同一图中显示不同的数据集&#xff0c;但它们具有相同的 x 轴数据时&#xff0c;可以使用 twinx() 函数创建一个新的坐标轴&#…

(五)、CT球管

第一代和第二代扫描仪使用固定阳极、油冷的X射线管&#xff0c;但由于对输出功率增加的需求&#xff0c;旋转阴极X射线管在CT中变得普遍。 传统固定阳极CT管&#xff0c;当阴极钨丝通过足够的电流时&#xff0c;使其产生白热现象时电子会从钨的表面逸出形成电子云&#xff0c;通…

Lazada菲律宾本土店选品怎么操作?EasyBoss ERP选品功能来帮你!

由于Lazada本土店在流量、履约速度、类目限制以及回款速度方面的优势&#xff0c;越来越多的Lazada卖家都在考虑转型做本土店&#xff0c;但本土化落地并不是一件容易的事&#xff0c;很多卖家在选品阶段就踩大坑了。 因此&#xff0c;为了选品不踩坑&#xff0c;很多卖家都会…

阿里云物联网自有app创建之初始化SDK

文章目录 一、新建工程&#xff0c;配置gradle,导入.so文件&#xff0c;生成apk二、上传apk&#xff0c;集成安全图片&#xff0c;下载SDK三、SDK的集成四、初始化SDK 最近在研究阿里云自有app,这是自己的心得。 一、新建工程&#xff0c;配置gradle,导入.so文件&#xff0c;生…

【论文阅读】超分辨率图像重建算法综述

0. 摘要 研究背景和意义 在人类视觉感知系统中&#xff0c;高分辨率&#xff08;HR&#xff09;图像对于清晰表达空间结构、细节特征、边缘纹理等信息至关重要&#xff0c;在医学、刑侦、卫星等多个领域具有广泛实用价值。超分辨率图像重建&#xff08;SRIR&#xff09;旨在从低…

MQTT vs HTTP:谁更适合物联网?

前言 随着物联网&#xff08;IoT&#xff09;技术的飞速发展中&#xff0c;其应用规模和使用场景正在持续扩大&#xff0c;但它关键的流程仍然是围绕数据传输来进行的&#xff0c;因此设备通信协议选择至关重要。 作为两种主要的通信协议&#xff0c;MQTT 协议和 HTTP 协议各…

AI提示工程:掌握高效Prompt设计的终极指南

导读 提示工程&#xff08;Prompt Engineering&#xff09;是一门新兴的学科&#xff0c;专注于提示词的开发和优化&#xff0c;旨在帮助用户在各种场景和研究领域中更好地利用大语言模型&#xff08;Large Language Model, LLM&#xff09;。掌握相关的提示工程技能将有助于用…

想提升发明专利审查速度有哪些快捷方法?

在创新驱动的时代&#xff0c;发明专利的审查速度对于企业和发明者来说至关重要。了解发明专利的审查程序以及掌握加快审查的快捷途径&#xff0c;能够帮助申请人更快地获得专利授权&#xff0c;保护其创新成果。 一、目前发明专利的审查程序 1. 申请提交&#xff1a;申请人需…

2024年10月上旬更新的6场AI大赛来了,总奖金池:22.9万

亲爱的朋友们&#xff0c;2024年10月的AI大赛最新资讯已上线&#xff01;不要错过这些精彩赛事&#xff0c;快来关注吧&#xff01; 本期我们为您精选了6场AI大赛&#xff0c;主要集中在AI绘画和AI视频创作领域&#xff0c;还有一场别开生面的开放式答题竞赛。这些大赛均由政府…

Java | Leetcode Java题解之第461题汉明距离

题目&#xff1a; 题解&#xff1a; class Solution {public int hammingDistance(int x, int y) {int s x ^ y, ret 0;while (s ! 0) {s & s - 1;ret;}return ret;} }

线性ADRC(LADRC)系统算法框图

非线性ADRC(NLADRC)详细算法框图和源代码请参考专栏系列文章,常用链接如下: 1、NLADRC自抗扰控制 NLADRC自抗扰控制从Simulink仿真到PLC控制实现_自抗扰控制器 simulink仿真-CSDN博客文章浏览阅读1.6k次,点赞2次,收藏7次。本文介绍了如何将ADRC自抗扰控制算法从Simulink…

基于Renesas R7FA8D1BH (Cortex®-M85)自适应蓝牙控制智能小车

目录 概述 1 系统框架结构 2 系统硬件介绍 2.1 电机驱动控制结构 2.1.1 PWM控制小车接口介绍 2.1.2 小车运行方向控制原理 2.2 外围传感器接口 2.2.1 I2C接口设备 2.2.2 IO接口 2.2.3 UART接口 2.2.4 其他接口 2.3 障碍物监测接口 2.4 测速模块接口 3 系统软件架构…

你还在为找不到免费录屏工具而烦恼吗?

嘿&#xff0c;各位小伙伴们&#xff0c;今天咱们来聊聊录屏软件吧&#xff01;你是不是经常需要录制电脑屏幕&#xff0c;做做教程、游戏解说或者工作汇报呢&#xff1f;那你肯定得选个好用的录屏工具啊&#xff01;来来来&#xff0c;跟着我一起&#xff0c;我来给你们推荐几…

SpringBoot项目打成jar包,在其他项目中引用

1、首先新建一个SpringBoot工程 记得要将Gradle换成Maven 2、新建一个要引用的方法 3、打包的时候要注意&#xff1a; ① 不能使用springboot项目自带的打包插件进行打包&#xff0c;下面是自带的&#xff1a; ②要换成传统项目的maven打包&#xff0c;如下图&#xff1a; 依…