使用贝塞尔曲线实现一个iOS时间轴

UI效果

请添加图片描述

实现的思路

就是通过贝塞尔曲线画出时间轴的圆环的路径,然后
使用CAShaper来渲染UI,再通过
animation.beginTime = [cilrclLayer convertTime:CACurrentMediaTime() fromLayer:nil] + circleTimeOffset 来设置每个圆环的动画开始时间,
,每个地方都是有两层layer的,一层是底部灰色样式的(即没有到达时候的颜色)一层是到达得阶段的颜色,
到达的layer在上面,如果要开启动画,我们就得先将
到达的layer隐藏掉,然后开始动画的时候,将对应的那一条展示,开启动画的时候将hidden置为NO,这时候,其实layer是展示不了的(不会整个展示),因为我们添加了strokeEnd的动画,他会随者动画的进行而逐渐展示,所以我们在动画代理方法animationDidStart中,将layer 设置为可见,(如果没有设置动画代理,也可以在添加动画的时候设置为可见)

代码

//
//  LBTimeView.m
//  LBTimeLine
//
//  Created by mac on 2024/6/9.
//#import "LBTimeView.h"
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
#define RGB(r, g, b)    [UIColor colorWithRed:(r)/255.f green:(g)/255.f blue:(b)/255.f alpha:1.f]
#define SizeScale (([UIScreen mainScreen].bounds.size.width > 320) ? [UIScreen mainScreen].bounds.size.width/320 : 1)const float BETTWEEN_LABEL_OFFSET = 20;
const float LINE_WIDTH = 1.9;
const float CIRCLE_RADIUS = 3.7;
const float INITIAL_PROGRESS_CONTAINER_WIDTH = 20.0;
const float PROGRESS_VIEW_CONTAINER_LEFT = 51.0;
const float VIEW_WIDTH = 225.0;@interface LBTimeView ()
{CGPoint lastpoint;NSMutableArray *layers;NSMutableArray *circleLayers;int layerCounter;int circleCounter;CGFloat timeOffset;CGFloat leftWidth;CGFloat rightWidth;CGFloat viewWidth;
}@end@implementation LBTimeView-(id)initWithFrame:(CGRect)frame sum:(NSInteger)sum current:(NSInteger)current{self = [super initWithFrame:frame];if (self) {self.frame = frame;[self configureTimeLineWithNum:sum andCurrentNum:current];}return self;// Do any additional setup after loading the view, typically from a nib.
}- (void)configureTimeLineWithNum:(NSInteger)sum andCurrentNum:(NSInteger)currentStatus {// NSInteger  currentStatus = 3;circleLayers = [[NSMutableArray alloc] init];layers = [[NSMutableArray alloc] init];CGFloat U = (ScreenWidth - 80- sum+1)/(sum - 1);CGFloat betweenLineOffset = 0;//CGFloat totlaHeight = 8;// CGFloat yCenter = - 48 + (ScreenWidth - 248)/2;CGFloat yCenter = 40;// CGFloat xCenter;UIColor *strokeColor;CGPoint toPoint;CGPoint fromPoint;int i = 0;for (int j = 0;j < sum;j ++) {//configure circlestrokeColor = i < currentStatus ? RGB(224, 0, 30) : RGB(233, 233, 233);UIBezierPath *circle = [UIBezierPath bezierPath];[self configureBezierCircle:circle withCenterY:yCenter];CAShapeLayer *circleLayer = [self getLayerWithCircle:circle andStrokeColor:strokeColor];//[circleLayers addObject:circleLayer];//add static background gray circleCAShapeLayer *grayStaticCircleLayer = [self getLayerWithCircle:circle andStrokeColor:RGB(233, 233, 233)];[self.layer addSublayer:grayStaticCircleLayer];[self.layer addSublayer:circleLayer];//configure lineif (i > 0) {fromPoint = lastpoint;toPoint = CGPointMake(yCenter - CIRCLE_RADIUS,60*SizeScale);lastpoint = CGPointMake( yCenter + CIRCLE_RADIUS+ 1,60*SizeScale);UIBezierPath *line = [self getLineWithStartPoint:fromPoint endPoint:toPoint];CAShapeLayer *lineLayer = [self getLayerWithLine:line andStrokeColor:strokeColor];// CAShapeLayer *lineLayer2 = [self getLayerWithLine:line andStrokeColor:strokeColor];[layers addObject:lineLayer];//add static background gray lineCAShapeLayer *grayStaticLineLayer = [self getLayerWithLine:line andStrokeColor:RGB(233, 233, 233)];[self.layer addSublayer:grayStaticLineLayer];[self.layer addSublayer:lineLayer];} else {lastpoint = CGPointMake( yCenter + CIRCLE_RADIUS+1,60*SizeScale);}betweenLineOffset = BETTWEEN_LABEL_OFFSET;yCenter += U;i++;}
}- (CAShapeLayer *)getLayerWithLine:(UIBezierPath *)line andStrokeColor:(UIColor *)strokeColor {CAShapeLayer *lineLayer = [CAShapeLayer layer];lineLayer.path = line.CGPath;lineLayer.strokeColor = strokeColor.CGColor;lineLayer.fillColor = nil;lineLayer.lineWidth = 1.4;return lineLayer;
}- (UIBezierPath *)getLineWithStartPoint:(CGPoint)start endPoint:(CGPoint)end {UIBezierPath *line = [UIBezierPath bezierPath];[line moveToPoint:start];[line addLineToPoint:end];return line;
}- (CAShapeLayer *)getLayerWithCircle:(UIBezierPath *)circle andStrokeColor:(UIColor *)strokeColor {CAShapeLayer *circleLayer = [CAShapeLayer layer];circleLayer.path = circle.CGPath;circleLayer.strokeColor = strokeColor.CGColor;circleLayer.fillColor = nil;circleLayer.lineWidth = LINE_WIDTH;circleLayer.lineJoin = kCALineJoinBevel;return circleLayer;
}- (void)configureBezierCircle:(UIBezierPath *)circle withCenterY:(CGFloat)centerY {[circle addArcWithCenter:CGPointMake( centerY,60*SizeScale)radius:CIRCLE_RADIUSstartAngle:M_PI_2endAngle:-M_PI_2clockwise:YES];[circle addArcWithCenter:CGPointMake(centerY,60*SizeScale)radius:CIRCLE_RADIUSstartAngle:-M_PI_2endAngle:M_PI_2clockwise:YES];
}- (void)startAnimatingWithCurrentIndex:(NSInteger)index
{for (CAShapeLayer *layer in layers) {layer.hidden = YES;}[self startAnimatingLayers:circleLayers forStatus:index];
}- (void)startAnimatingLayers:(NSArray *)layersToAnimate forStatus:(NSInteger)currentStatus {float circleTimeOffset = 1;circleCounter = 0;int i = 1;//add with animationfor (CAShapeLayer *cilrclLayer in layersToAnimate) {[self.layer addSublayer:cilrclLayer];CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];animation.duration = 0.2;animation.beginTime = [cilrclLayer convertTime:CACurrentMediaTime() fromLayer:nil] + circleTimeOffset;animation.fromValue = [NSNumber numberWithFloat:0.0f];animation.toValue   = [NSNumber numberWithFloat:1.0f];animation.fillMode = kCAFillModeForwards;animation.delegate =(id <CAAnimationDelegate>) self;circleTimeOffset += .4;[cilrclLayer setHidden:YES];[cilrclLayer addAnimation:animation forKey:@"strokeCircleAnimation"];if (self.lastBlink && i == currentStatus && i != [layersToAnimate count]) {CABasicAnimation *strokeAnim = [CABasicAnimation animationWithKeyPath:@"strokeColor"];strokeAnim.fromValue         = (id) [UIColor orangeColor].CGColor;strokeAnim.toValue           = (id) [UIColor clearColor].CGColor;strokeAnim.duration          = 1.0;strokeAnim.repeatCount       = HUGE_VAL;strokeAnim.autoreverses      = NO;[cilrclLayer addAnimation:strokeAnim forKey:@"animateStrokeColor"];}i++;}
}- (void)animationDidStart:(CAAnimation *)anim {if (circleCounter < circleLayers.count) {if (anim == [circleLayers[circleCounter] animationForKey:@"strokeCircleAnimation"]) {[circleLayers[circleCounter] setHidden:NO];circleCounter++;}}
}- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {if (layerCounter >= layers.count) {return;}CAShapeLayer *lineLayer = layers[layerCounter];CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];animation.duration = 0.200;animation.fromValue = [NSNumber numberWithFloat:0.0f];animation.toValue   = [NSNumber numberWithFloat:1.0f];animation.fillMode = kCAFillModeForwards;lineLayer.hidden = NO;[self.layer addSublayer:lineLayer];[lineLayer addAnimation:animation forKey:@"strokeEndAnimation"];layerCounter++;
}@end

如果对您有帮助,欢迎给一个star
demo

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

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

相关文章

探索ChatGPT-4在解决化学知识问题上的研究与应用

1. 概述 近年来&#xff0c;人工智能的发展主要集中在 GPT-4 等大型语言模型上。2023 年 3 月发布的这一先进模型展示了利用广泛知识应对从化学研究到日常问题解决等复杂挑战的能力。也开始进行研究&#xff0c;对化学的各个领域&#xff0c;从化学键到有机化学和物理化学&…

【设计模式】行为型设计模式之 备忘录模式(快照模式)

介绍 备忘录应用场景明确并且有限&#xff0c;一般用来数据的防丢失、撤销和恢复。对大对象的备份和恢复&#xff0c;备忘录模式能有效的节省时间和空间开销。 定义 备忘录模式&#xff1a;也称为快照模式&#xff0c;在不违背封装原则的前提下&#xff0c;捕获一个对象的内…

BFS实现图的点的层次-java

加强对广度优先搜索的理解&#xff0c;其实就是主要的3个步骤&#xff0c;外加数组模拟单链表是基础&#xff0c;要搞懂。 目录 前言 一、图中点的层次 二、算法思路 1.广度优先遍历 2.算法思路 三、代码如下 1.代码如下&#xff08;示例&#xff09;&#xff1a; 2.读入…

探索基于订阅式的电视App:Android TV 端强大的开源视频播放器

探索基于订阅式的电视App&#xff1a;Android TV 端强大的开源视频播放器 在智能电视和流媒体日益普及的今天&#xff0c;一款强大的视频播放器是家庭娱乐的重要组成部分。正是这样一款为Android TV设计的开源视频播放器。本文将深入探讨电视盒子OSC的技术特点、使用方法以及其…

抛弃昂贵BI,企业仍可低成本实现数据分析

有的读者看完《BI工具选型不入坑&#xff0c;你要这么选》这篇文章就陷入迷茫了&#xff0c;我要做企业级数据分析&#xff0c;看过去各家产品都各有千秋&#xff0c;实在难以抉择&#xff0c;或者已经选了仍是纠结不已。 这里我抛出另一种思路&#xff1a;如果不用BI&#xf…

C#操作MySQL从入门到精通(17)——使用联结

前言: 我们在查询数据的过程中有时候查询的数据不是来自一个表而是来自多个表,本文使用的测试数据如下: 本文使用了两个表student_info、address_info student_info的数据如下: address_info的数据如下: 1、内联结 所谓内联结就是求交集,两个表都有的数据才是有效数…

94、二叉树的迭代遍历

实现对二叉树的前后序非递归遍历 题解&#xff1a; 递归的实现就是&#xff1a;递去&#xff0c;归来。每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中&#xff0c;然后递归返回的时候&#xff0c;从栈顶弹出上一次递归的各项参数&#xff0c;所以这就是…

46.django - 多语言配置

1.Django 多语言基础知识 多语言站点可以让不同语言的用户更好地使用和理解网站内容&#xff0c;提升用户体验和覆盖范围。为了实现多语言功能&#xff0c;我们将使用Django内置的国际化和本地化支持。我收集了一些知识点整理在这一部分&#xff0c;感兴趣的可以看看。直接跳过…

安装搭建java版的悟空crm遇到 网络错误请稍候再试 终极解决办法(hrm人力资源模块)

java版 项目目录 ├── build – webpack 配置文件 ├── config – 项目配置文件 ├── src – 源码目录 │ ├── api – axios请求接口 │ ├── assets – 静态图片资源文件 │ ├── components – 通用组件 │ ├── directives – 通用指令 │ ├── filters –…

项目中的任务调度和消息队列方案详解

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 任务…

软件项目调研三部曲(调研计划,调研大纲,调研报告)原件获取

软件项目详细调研的目的是依据项目合同的要求&#xff0c;能够通过在客户现场&#xff0c;并通过访谈的方式&#xff0c;尽可能的了解客户的需求和业务现状&#xff0c;包括客户涉及到的业务部门的岗位设置、岗位职责、工作内容、工作流程、管理难点以及通过实施信息化系统的期…

GDPU Java 天码行空15 数据库编程

一、实验目的 1、 了解数据库的基础知识。 2、 掌握MySQL的下载、安装与配置。 3、 掌握MySQL可视化工具的使用。 4、 了解SQL语言。 5、 掌握JDBC中的API&#xff0c;并能进行简单的数据库操作。 二、实验内容 1、 安装MySQL &#x1f468;‍&#x1f3eb; 视频教程 2、建…

华为云加码数智升级 赋能山西培育新质生产力

加码企业数智升级&#xff0c;驱动山西新质生产力跃升。6月4日&#xff0c;“云上共创晋而有为”华为云山西数智升级思享会在山西智创城NO.1创新孵化基地举办。 活动由华为云计算技术有限公司主办&#xff0c;太原清控科创科技园管理有限公司协办。活动整合山西鲲鹏生态创新中…

嵌入式单片机中项目在线仿真工具分享

前段时间,无意间发现了一个不错的在线仿真工具(Wokwi),支持多种平台,支持市面上主流的开发板,比如:STM32、ESP32、Arduino、树莓派等。 还支持常见的传感器、显示器件(LCD、LED屏幕)等,还可以播放音乐、联网、逻辑分析仪等,关键还提供了很多实际项目的案例。 这款工…

Cocos2dx 编译游戏安装包制作教程

在本教程中&#xff0c;我们将学习如何为 Visual Studio 编译项目配置图标&#xff0c;并使用 Inno Setup 创建安装包。我们将包括以下部分&#xff1a; 设置项目图标&#xff1a;在 Visual Studio 中配置 .exe 文件的图标&#xff0c;或者使用第三方工具替换编译后的图标。创…

简记:为Docker配置服务代理

简记 为Docker配置服务代理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/art…

小冬瓜AIGC 手撕LLM 拼课

小冬瓜aigc手撕LLM学习 官方认证 手撕LLMRLHF速成班-(附赠LLM加速分布式训练超长文档&#xff09; 帮助多名同学上岸LLM方向&#xff0c;包括高校副教授&#xff0c;北美PhD&#xff0c;大厂等 课程名称【手撕LLMRLHF】 授课形式&#xff1a;在线会议直播讲解课后录播 时间&…

RK3568笔记三十一:ekho 6.3 文本转语音移植

若该文为原创文章&#xff0c;转载请注明原文出处。 移植的目的是在在OCR识别基础上增加语音播放&#xff0c;把识别到的文字直接转TTS播报出来&#xff0c;形成类似点读机的功能。 1、下载文件 libsndfile-1.0.28.tar.gz ekho-6.3.tar.xz 2、解压 tar zxvf libsndfile-1.0…

LangChain基础知识入门

LangChain的介绍和入门 1 什么是LangChain LangChain由 Harrison Chase 创建于2022年10月&#xff0c;它是围绕LLMs&#xff08;大语言模型&#xff09;建立的一个框架&#xff0c;LLMs使用机器学习算法和海量数据来分析和理解自然语言&#xff0c;GPT3.5、GPT4是LLMs最先进的代…

【源码】Spring Data JPA原理解析之事务注册原理

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…