当前位置: 首页 > article >正文

visionOS空间计算实战开发教程Day 11 标题动画

本文我们要在visionOS内实现一个标题输出的动画效果。主要讲​​ViewModifier​​​协议,修饰符(modifier)应用于视图或另一个视图修饰符,生成原值的另一个版本。在希望创建一个可应用于不同视图的修饰符时可实现​​ViewModifier​​协议。

首先定义​​ViewModel​​,本例中的模型比较简单,仅定了三个变量,分别表示当前文本、标题输出是否完成以及最终的标题文本。

import SwiftUI

@Observable
class ViewModel {
    var titleText: String = ""
    var isTitleFinished: Bool = false
    var finalTitle: String = "第三回 托内兄如海荐西宾 接外孙贾母惜孤女"
}

因模型中有默认值且需要在程序运行的过程中进行修改,所以在入口文件中需要将模型注入到环境中:

import SwiftUI

@main
struct visionOSDemoApp: App {
    @State private var model = ViewModel()
    var body: some Scene {
        WindowGroup() {
            ContentView()
                .environment(model)
        }
    }
}

接下来就是本文的重点了,我们需要自定义一个文本修饰符。虽然可以直接将修饰符应用于视图,但更常见和地道的做法是使用修饰符来定义一个​​View​​来包装这个视图修饰符。我们在代码里就是这么做的,在视图中我们传入了5个变量,​​text​​和​​isFinished​​是需要进行修改的,所以使用了​​Binding​​,​​cursor​​定义了光标,默认使用了常见的​​|​​,​​isAnimated​​表示是否显示动画。

在​​TypeTextModifier​​中可以看到,如果​​isAnimated​​为​​false​​,就直接显示最终文本。而在任务中有两个​​for​​循环,分别设置初始的光标闪烁效果以及后续逐个文字和光标交替输出的效果,最后等待片刻,标记输出结束。

import SwiftUI

extension View {
    func typeText(
        text: Binding<String>,
        finalText: String,
        isFinished: Binding<Bool>,
        curor: String = "|",
        isAnimated: Bool = true
    ) -> some View {
        self.modifier(
            TypeTextModifier(
                text: text,
                finalText: finalText,
                isFinished: isFinished,
                cursor: curor,
                isAnimated: isAnimated
            )
        )
    }
}

private struct TypeTextModifier: ViewModifier {
    @Binding var text: String
    var finalText: String
    @Binding var isFinished: Bool
    var cursor: String
    var isAnimated: Bool
    
    func body(content: Content) -> some View {
        content
            .onAppear {
                if isAnimated == false {
                    text = finalText
                    isFinished = true
                }
            }
            .task {
                guard isAnimated else { return }
                
                // Blink the cursor a few times
                for _ in 1...2 {
                    text = cursor
                    try? await Task.sleep(for: .milliseconds(500))
                    text = ""
                    try? await Task.sleep(for: .milliseconds(200))
                }
                
                // Type out the title
                for index in finalText.indices {
                    text = String(finalText.prefix(through: index)) + cursor
                    let milliseconds = (1 + UInt64.random(in: 0...1)) * 100
                    try? await Task.sleep(for: .milliseconds(milliseconds))
                }
                
                // Wrap up the title sequence
                try? await Task.sleep(for: .milliseconds(400))
                text = finalText
                isFinished = true
            }
    }
}

​ContentView​​内容如下:

struct ContentView: View {
    @Environment(ViewModel.self) private var model
    
    var body: some View {
        @Bindable var model = model
        
        NavigationStack {
            VStack {
                Spacer()
                VStack {
                    Text(model.finalTitle)
                        .monospaced()
                        .font(.system(size: 50, weight: .bold))
                        .padding(.horizontal, 40)
                        .hidden()
                        .overlay(alignment: .leading) {
                            Text(model.titleText)
                                .monospaced()
                                .font(.system(size: 50, weight: .bold))
                                .padding(.leading, 40)
                        }
                    Text("林黛玉进贾府")
                        .font(.title)
                        .padding(.top, 10)
                        .opacity(model.isTitleFinished ? 1 : 0)
                }
                Spacer()
            }
            .typeText(text: $model.titleText, finalText: model.finalTitle, isFinished: $model.isTitleFinished, isAnimated: !model.isTitleFinished)
        }
    }
}

这里在屏幕中央输出两段文本,第一段会以修饰符的动画效果进行输出直至结束,第二段在第一段文本输出完成后显示。

visionOS空间计算实战开发教程Day 11 标题动画

示例代码:​​GitHub仓库​​

其它相关内容请见​​虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记​​


http://www.kler.cn/news/159764.html

相关文章:

  • 记录 | linux静态库和动态库的理解
  • 3.4_1 java自制小工具 - pdf批量转图片
  • 数字化时代的保镖:实人认证API在身份验证中的角色
  • 【Redis】redis 高性能--线程模型以及epoll网络框架
  • 每日一题(LeetCode)----字符串--重复的子字符串
  • photoshop实现抠图的步骤
  • 《golang设计模式》第三部分·行为型模式-09-策略模式(Strategy)
  • Python学习路线 - Python语言基础入门 - 判断语句
  • 【langchain实战】开源项目-RasaGPT
  • 浏览器的favicon.icon 消失不见解决方案
  • 浏览器判断某一个 ip 是否与其在同一个局域网的几种方式
  • S120和PLC通讯设置
  • EDW国际数据管理最新趋势(二)|信息供应链与数据
  • 人类长寿新突破背后:“原知因”成为炙手可热新课题
  • heatmap.js热力图【vue3】
  • 【开题报告】基于SpringBoot的农场管理系统的设计与实现
  • 用OpenCV与MFC写一个图像格式转换及简单处理程序
  • Win11安装Docker
  • 【数据库】数据库多种锁模式,共享锁、排它锁,更新锁,增量锁,死锁消除与性能优化
  • 二叉树链式结构的实现——C语言
  • PG 常用维护性 SQL
  • git报错invalid object xxx和unable to read tree xxxxxx
  • 播放器开发(六):音频帧处理并用SDL播放
  • 三部曲法求未定式极限中的1无穷次方型
  • 【探索Linux】—— 强大的命令行工具 P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
  • 【教程】Conda更换镜像源安装pytorch
  • Git篇如何搭建自己的git仓库
  • 前端知识笔记(二十五)———JS中的异步编程与Promise
  • 如何给自己的网站加密
  • C++大小写字母转换