Timer/计时器发布者与订阅者, Subscribar/文本框订阅模式 的使用

news2024/11/15 21:56:11

1. Timer 计时器的操作

  1.1 实现

/// 计时器 发布者与订阅者
struct TimerBootcamp: View {
    
    //  计时器 发布者 timer1/timer3/timer5 = 1.0 , timer2 = 2.0 , timer4 = 0.5
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    // ---1---
    // 计算性属性 当前时间
    @State var currentDate = Date()
    var dateFormatter: DateFormatter{
        let formatter = DateFormatter()
        //formatter.dateStyle = .medium
        formatter.timeStyle = .medium
        return formatter
    }
    
    // ---2---
    // CountDown 倒计时
    @State var countDown: Int = 10
    @State var finishedText: String? = nil
    
    // ---3---
    // 倒计时日期 Countdown to date
    @State var timeRemaining: String = ""
    // 将来时间 提前一天
    let futureDate:Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date()
    // 更新时间
    func updateTimeRemaRemaining(){
        //.hour,
        let remaining = Calendar.current.dateComponents([.minute, .second], from: Date(), to: futureDate)
        //let hour = remaining.hour ?? 0
        let minute = remaining.minute ?? 0
        let second = remaining.second ?? 0
        //\(hour) :
        timeRemaining = " \(minute) minutes, \(second) seconds"
    }
    
    // ---4---
    // 动画计数器 Animation counter
    @State var countCircle: Int = 0
    
    // ---5---
    // 动画 page
    @State var count: Int = 1
    
    // 背景View
    var backgroundView: some View{
        // 渐变
        RadialGradient(
            gradient: Gradient(colors: [Color(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1)), Color(#colorLiteral(red: 0.09019608051, green: 0, blue: 0.3019607961, alpha: 1))]),
            center: .center,
            startRadius: 5,
            endRadius: 500)
        .ignoresSafeArea()
    }
    
    var body: some View{
        //timer1
        //timer2
        //timer3
        //timer4
        timer5
    }
    
    // 方式五
    var timer5: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            TabView(selection: $count) {
                Rectangle()
                    .foregroundColor(.red)
                    .tag(1)
                Rectangle()
                    .foregroundColor(.blue)
                    .tag(2)
                Rectangle()
                    .foregroundColor(.green)
                    .tag(3)
                Rectangle()
                    .foregroundColor(.orange)
                    .tag(4)
                Rectangle()
                    .foregroundColor(.gray)
                    .tag(5)
            }
            .frame(height: 200)
            .tabViewStyle(.page)
        }
        // 监听计时器
        .onReceive(timer) { _ in
            // 添加动画
            withAnimation(.default){
                count = count == 5 ? 1 : count + 1
            }
        }
    }
    
    // 方式四
    var timer4: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            HStack(spacing: 15) {
                Circle()
                    .offset(y: countCircle == 1 ? -20 : 0)
                Circle()
                    .offset(y: countCircle == 2 ? -20 : 0)
                Circle()
                    .offset(y: countCircle == 3 ? -20 : 0)
            }
            .frame(width: 150)
            .foregroundColor(.white)
        }
        // 监听计时器
        .onReceive(timer) { _ in
            // 添加动画
            withAnimation(.easeInOut(duration: 0.5)){
                countCircle = countCircle == 3 ? 0 : countCircle + 1
            }
        }
    }
    
    // 方式三
    var timer3: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            // 显示文字
            Text(timeRemaining)
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.white)
                .lineLimit(1) // 保持1行
                .minimumScaleFactor(0.1) // 最小比例因子为 0.1
        }
        // 监听计时器
        .onReceive(timer) { _ in
            updateTimeRemaRemaining()
        }
    }
    
    // 方式二
    var timer2: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            // 显示文字
            Text(finishedText ?? "\(countDown)")
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.white)
                .lineLimit(1) // 保持1行
                .minimumScaleFactor(0.1) // 最小比例因子为 0.1
        }
        // 监听计时器
        .onReceive(timer) { _ in
            if countDown <= 1 {
                finishedText = "Wow!"
            }else{
                countDown -= 1
            }
        }
    }
    
    // 方式一
    var timer1: some View{
        ZStack {
            // 背景View
            backgroundView
            
            // 显示文字
            Text(dateFormatter.string(from: currentDate))
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.white)
                .lineLimit(1) // 保持1行
                .minimumScaleFactor(0.1) // 最小比例因子为 0.1
        }
        // 监听计时器
        .onReceive(timer) { output in
            currentDate = output
        }
    }
}

  1.2 效果图:

     

          

2. Subscribar 输入文本框监听文本订阅者模式

  2.1 实现

import Combine

/// ViewModel
class SubscribarViewModel: ObservableObject{
    @Published var count: Int = 0
    
    // var timer: AnyCancellable?
    var canncellables = Set<AnyCancellable>()
    
    // 发布者: 可以订阅发布者改动时发布的值
    @Published var textFieldText: String = ""
    @Published var textIsValid: Bool = false
    @Published var showButton: Bool = false
    
    init() {
        setUpTimer()
        addTextFieldSubscriber()
        addButtonSubscriber()
    }
    
    // 设置计时器
    func setUpTimer(){
        // 定时发送  timer =
        Timer
            .publish(every: 1, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                guard let self = self else { return }
                self.count += 1
                //if self.count >= 10 {
                //self?.count = 0
                //self.timer?.cancel()
                //for item in canncellables{
                //    item.cancel()
                //}
                //}
            }
            .store(in: &canncellables)
    }
    
    // 输入文本订阅者
    func addTextFieldSubscriber(){
        $textFieldText
        // debounce 去抖动 输入停止时,隔 0.5 秒执行后面的代码
            .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
            .map { text in
                if text.count > 3 {
                    return true
                }
                return false
            }
        // 不建议使用 .assign ,因为使用的是强引用,没法修改 ,建议使用 .sink
        //.assign(to: \.textIsValid, on: self)
            .sink(receiveValue: {[weak self] isValid in
                guard let self = self else { return }
                self.textIsValid = isValid
            })
        // 可随时取消
            .store(in: &canncellables)
    }
    
    // 添加按钮订阅者
    func addButtonSubscriber(){
        $textIsValid
        // 组合最新值
            .combineLatest($count)
            .sink { [weak self] isValid, count in
                guard let self = self else { return }
                if isValid && count >= 5 {
                    self.showButton = true
                }else{
                    self.showButton = false
                }
            }
            .store(in: &canncellables)
    }
}

// 订阅者模式
struct SubscriberBootcamp: View {
    @StateObject var viewModel = SubscribarViewModel()
    
    var body: some View {
        VStack {
            Text("\(viewModel.count)")
                .font(.largeTitle)
            
            TextField("Type something here...", text: $viewModel.textFieldText)
                .frame(height: 55)
                .padding(.horizontal)
                .font(.headline)
                .background(Color.gray.opacity(0.3))
                .cornerRadius(10)
                .overlay(alignment: .trailing) {
                    ZStack() {
                        Image(systemName: "xmark")
                            .foregroundColor(.red)
                            .opacity(viewModel.textFieldText.count < 1 ? 0.0:
                                        viewModel.textIsValid ? 0.0 : 1.0)
                        Image(systemName: "checkmark")
                            .foregroundColor(.accentColor)
                            .opacity(viewModel.textIsValid ? 1.0 : 0.0)
                    }
                    .font(.title)
                    .padding(.trailing)
                }
            
            Button {
            } label: {
                Text("Submit")
                    .font(.headline)
                    .foregroundColor(.white)
                    .frame(height: 55)
                    .frame(maxWidth: .infinity)
                    .background(Color.accentColor)
                    .cornerRadius(10)
                    .opacity(viewModel.showButton ? 1.0 : 0.5)
            }
            .disabled(!viewModel.showButton)
        }
        .padding()
    }
}

  2.2 效果图:

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

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

相关文章

线性代数(七) 矩阵分析

前言 从性线变换我们得出&#xff0c;矩阵和函数是密不可分的。如何用函数的思维来分析矩阵。 矩阵的序列 通过这个定义我们就定义了矩阵序列的收敛性。 研究矩阵序列收敛性的常用方法&#xff0c;是用《常见向量范数和矩阵范数》来研究矩阵序列的极限。 长度是范数的一个特…

C语言实现---通讯录

通讯录 前言1.初始化通讯录2.SHOW展示通讯录3.ADD添加联系人的函数4.DEL删除联系人的函数5.SEARCH查找联系人的函数6.MODIFY修改联系人的函数7.销毁通讯录8.整体代码展示1.头文件"contact.h"2.源文件"contact.c"3.测试文件"test.c" 前言 那么好…

解决键盘长按才有反应

问题分析&#xff1a; 排除键盘自身没有问题&#xff0c;那就应该是电脑设置的问题&#xff0c;目前我遇到的不小心长按shift键设置了【筛选键】 解决方式&#xff1a; 打开设置搜索筛选建&#xff0c;关闭&#xff0c;粘带键也会出现类似问题。

【go语言】结构体

结构体 结构体定义 type name struct{value1 type1value2 type2...... }组成结构体的数据称为字段&#xff0c;每一个字段有一个类型和一个名字&#xff0c;每一个字段的名字必须唯一&#xff0c;字段的类型任意。 创建结构体 type myStruct struct{i1 intf1 float32str st…

【软件测试】开发/测试模型

开发/测试模型 瀑布模型 设计&#xff1a;技术文档(设计那些接口&#xff0c;库表&#xff0c;mq&#xff0c;定时任务)&#xff0c;UI视觉稿 特点&#xff1a;线性的结构。 优点&#xff1a;每个阶段做什么&#xff0c;产出什么非常清晰 缺点&#xff1a;测试人员介入太晚…

了解CISP,看这篇文章就够了,附上CISP证书题库资料

前言 【点击此处领取CISP题库资料和网络安全学习资】 什么是CISP&#xff1f; CISP是中国信息安全测评中心依据中编办赋予的职能&#xff0c;建立和发展的一整套完整的信息安全保障人才培训体系&#xff0c;从2002年开始启动。 CISP (CertifiedInformation Security Profes…

分享55个C源码源代码总有一个是你想要的

分享55个C源码源代码总有一个是你想要的 链接&#xff1a;https://pan.baidu.com/s/1_zbaunqvmYRhCiX7hbiqmg?pwd8888 提取码&#xff1a;8888 1. 项目名称 apachesubversion版本控制系统 v1.10.3 Ceph分布式文件系统 v17.2.5 clip命令行插图处理器 v0.8 curve分布式存…

在前端设计中,子元素的基线和父元素的基线分别是什么意思?并利用Bootstrap的类align-items-baseline实现子元素在其父容器内基线对齐。

子元素的基线和父元素的基线是用于文本对齐的重要概念。让我解释一下它们分别指的是什么&#xff1a; 子元素的基线&#xff08;Baseline of Child Elements&#xff09;&#xff1a; 子元素的基线是指子元素内文本的底部边缘&#xff0c;特别是字母的底部边缘。在包含文本的元…

医药行业电力供应3大难题?教你如何破解

电力是现代社会不可或缺的基础设施之一&#xff0c;它支持着工业、商业和生活的各个方面。在这个数字化、电气化的时代&#xff0c;电力配电系统扮演着关键的角色&#xff0c;确保电能以可靠、高效、安全的方式分发到我们的家庭、企业和工厂。 然而&#xff0c;要保持电力分配的…

LeetCode 518.零钱兑换II 动态规划 + 完全背包 + 组合数

给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带符号整数。 示例…

DevOps持续集成与交付

概述 Jenkins是一个支持容器化部署的、使用Java运行环境的开源软件&#xff0c;使用Jenkins平台可以定制化不同的流程与任务、以自动化的机制支持DevOps领域中的CI与CD&#xff0c;在软件开发与运维的流程中自动化地执行软件工程项目的编译、构建、打包、测试、发布以及部署&a…

FileManager/本地文件增删改查, Cache/图像缓存处理 的操作

1. FileManager 本地文件管理器&#xff0c;增删改查文件 1.1 实现 // 本地文件管理器 class LocalFileManager{// 单例模式static let instance LocalFileManager()let folderName "MyApp_Images"init() {createFolderIfNeeded()}// 创建特定应用的文件夹func cr…

在英文电脑系统中,中文显示??????

如果操作系统是英文的&#xff0c;那么无论是在cmd界面&#xff0c;还是在Visual Studio的调试界面&#xff0c;中文显示都是一串问号??????? 这是因为在英文系统中&#xff0c;Console 的默认代码页是 437(OEM -United States)&#xff0c;不支持中文输入输出&#xff…

照片后期处理软件DxO FilmPack 6 mac中文说明

DxO FilmPack 6 for Mac是一款照片后期处理软件。它可以模拟超过60种著名胶片品牌和类型的色彩和颗粒感&#xff0c;使照片具有复古、艺术和时尚风格。 ​DxO FilmPack 6 mac支持RAW和JPG格式的照片&#xff0c;并提供丰富的调整选项&#xff0c;如亮度、对比度、曝光、阴影和高…

十六.镜头知识之工业镜头的质量判断因素

十六.镜头知识之工业镜头的质量判断因素 文章目录 十六.镜头知识之工业镜头的质量判断因素1.分辨率(Resolution)2.明锐度(Acutance)3.景深(DOF)&#xff1a;4. 最大相对孔径与光圈系数5.工业镜头各参数间的相互影响关系5.1.焦距大小的影响情况5.2.光圈大小的影响情况5.3.像场中…

【Java 进阶篇】深入理解SQL查询语言(DQL)

SQL&#xff08;Structured Query Language&#xff09;是一种用于管理关系型数据库的强大编程语言。它提供了各种命令和语句&#xff0c;用于执行各种操作&#xff0c;包括数据查询、插入、更新和删除。本文将深入探讨SQL查询语言&#xff08;DQL&#xff09;&#xff0c;它是…

Bootstrap的弹性盒子布局学习笔记

Bootstrap的弹性盒子布局学习笔记 目录 01-综述02-利用类d-flex与类d-inline-flex将容器定义为弹性盒子03-对弹性容器的的元素在水平方向上进行排列顺序设置03-对弹性容器的的元素在垂直方向上进行排列顺序设置04-弹性盒子内所有元素在主轴方向上的对齐方式05-1-弹性盒子内各行…

myArm 全新七轴桌面型机械臂

引言 在不断演进的科技世界中&#xff0c;我们始终追求创新和卓越&#xff0c;以满足客户的需求并超越他们的期望。今天&#xff0c;我们很高兴地宣布我们的最新产品——myArm 300 Pi&#xff0c;一款七轴的桌面型机械臂。这款产品的独特之处在于其灵活性和可编程性&#xff0c…

16. Seata 分布式事务

Spring Cloud 微服务系列文章&#xff0c;点击上方合集↑ 1. 简介 Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。 事务是保障一系列操作要么都成功&#xff0c;要么都失败。就比如转账&#xff1a;A转账100元给B&#xff0…

sentinel-dashboard-1.8.0.jar开机自启动脚本

启动阿里巴巴的流控组件控制面板需要运行一个jar包&#xff0c;通常需要运行如下命令&#xff1a; java -server -Xms4G -Xmx4G -Dserver.port8080 -Dcsp.sentinel.dashboard.server127.0.0.1:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard-1.8.0.jar &…