使用贝塞尔曲线实现一个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
相关文章:
使用贝塞尔曲线实现一个iOS时间轴
UI效果 实现的思路 就是通过贝塞尔曲线画出时间轴的圆环的路径,然后 使用CAShaper来渲染UI,再通过 animation.beginTime [cilrclLayer convertTime:CACurrentMediaTime() fromLayer:nil] circleTimeOffset 来设置每个圆环的动画开始时间, …...
【深度学习】深度学习之巅:在 CentOS 7 上打造完美Python 3.10 与 PyTorch 2.3.0 环境
【深度学习】深度学习之巅:在 CentOS 7 上打造完美Python 3.10 与 PyTorch 2.3.0 环境 大家好 我是寸铁👊 总结了一篇【深度学习】深度学习之巅:在 CentOS 7 上打造完美Python 3.10 与 PyTorch 2.3.0 环境✨ 喜欢的小伙伴可以点点关注 &#…...
在docker容器中使用gdb调试python3.11的进程
gdb调试python进程的前提条件 安装python及python调试信息安装gdb工具安装python-gdb.py扩展 安装过程 我们使用docker来安装以上内容,Dockerfile文件内容如下: FROM docker.io/centos:7.4.1708# 安装依赖 RUN yum install -y -q epel-release &…...
堆排序要点和难点以及具体案例应用
堆排序(Heap Sort)是一种基于堆数据结构的排序算法。下面我将以分点表示和归纳的方式,结合相关数字和信息,详细描述堆排序的PTA(Programming and Testing Approach,编程与测试方法)。 1. 堆排序原理 堆排序是一种树形选择排序,利用了完全二叉树的性质,通过构建最大堆…...
pyspark中使用mysql jdbc报错java.lang.ClassNotFoundException: com.mysql.jdbc.Driver解决
报错信息: py4j.protocol.Py4JJavaError: An error occurred while calling o33.load. : java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 我的解决方法: 这个报错就是提示你找不到jar包,所以你需要去下载一个和你mysql版本匹配的j…...
对称加密系统解析
目录 1.概述 2. 对称密码类型 3. 对称加密优缺点 4. 对称加密算法 4.1 DES 4.2 3DES 4.3 AES 4.4 SM1 4.5 SM4 1.概述 对称加密,是指在加密和解密时使用同一秘钥的方式。秘钥的传送和保存的保护非常重要,务必不要让秘…...
初识 java 2
1. idea 的调试 1. 点击鼠标左键设置断点 2.运行到断点处 点击 或点击鼠标右键,再点击 使代码运行到断点处,得到 2. 输出到控制台 System.out.println(value);//输出指定的内容,并换行 value 要打印的内容System.out.print(value);…...
云端狂飙:Django项目部署与性能优化的极速之旅
Hello,我是阿佑,这次阿佑将手把手带你亲自踏上Django项目从单机到云端的全过程,以及如何通过Docker实现项目的无缝迁移和扩展。不仅详细介绍了Docker的基本概念和操作,还深入探讨Docker Compose、Swarm和Kubernetes等高级工具的使…...
GDPU JavaWeb 大结局篇(持续更新中)
GDPUJavaWeb程序设计复习,习题集,重点知识总结,一篇就够了。 实验复习 JavaWeb代码复习,在专栏也可查阅。 课后巩固习题 1 【单选题】下列说法正确的是( D ) A、在B/S结构中,结果应用软件发生了改变,就必须通知所有的客户端重新…...
Linux系统信息的查看
目录 前言一、系统环境二、查看系统IP地址信息2.1 ifconfig命令2.2 ip address命令 三、查看系统端口信息3.1 nmap命令3.2 netstat命令 四、查看系统进程信息4.1 ps命令4.2 kill命令 五、查看系统监控信息5.1 top命令5.2 df命令iostat命令5.3 sar命令 总结 前言 本篇文章介绍查…...
LE Audio音频广播新功能Auracast介绍
LE Audio音频广播新功能Auracast介绍 /*! \copyright Copyright (c) 2019-2022 Qualcomm Technologies International, Ltd. All Rights Reserved. Qualcomm Technologies International, Ltd. Confidential and Proprietary. \file audio_sources.h \defgroup audio_so…...
一文学习yolov5 实例分割:从训练到部署
一文学习yolov5 实例分割:从训练到部署 1.模型介绍1.1 YOLOv5结构1.2 YOLOv5 推理时间 2.构建数据集2.1 使用labelme标注数据集2.2 生成coco格式label2.3 coco格式转yolo格式 3.训练3.1 整理数据集3.2 修改配置文件3.3 执行代码进行训练 4.使用OpenCV进行c部署参考文…...
【设计模式】行为型设计模式之 策略模式学习实践
介绍 策略模式(Strategy),就是⼀个问题有多种解决⽅案,选择其中的⼀种使⽤,这种情况下我们 使⽤策略模式来实现灵活地选择,也能够⽅便地增加新的解决⽅案。⽐如做数学题,⼀个问题的 解法可能有…...
lua中大数相乘的问题
math.maxinteger * 2 --> -2 原因:math.maxinteger的二进制 : 0111111111111111111111111111111111111111111111111111111111111111 往左移位,最右加一个0,是 1111111111111111111111111111111111111111111111111111111111111…...
第一个SpringBoot项目
目录 💭1、新建New Project IDEA2023版本创建Sping项目只能勾选17和21,却无法使用Java8?🌟 2、下载JDK 17🌟 💭2、项目创建成功界面 1、目录 🌟 2、pom文件🌟 💭3、…...
Android 10.0 Launcher修改density禁止布局改变功能实现
1.前言 在10.0的系统rom定制化开发中,在关于Launcher3的定制化功能中,在有些功能需要要求改变系统原有的density屏幕密度, 这样就会造成Launcher3的布局变化,所以就不符合要求,接下来就来看下如何禁止改变density造成Launcher3布局功能 改变的实现 2.Launcher修改densit…...
CAN协议简介
协议简介 can协议是一种用于控制网络的通信协议。它是一种基于广播的多主机总线网络协议,常用于工业自动化和控制领域。can协议具有高可靠性、实时性强和抗干扰能力强的特点,被广泛应用于汽车、机械、航空等领域。 can协议采用了先进的冲突检测和错误检测…...
(二)JSX基础
什么是JSX 概念:JSX是JavaScript和XML(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中编写UI模板的方式。 优势:1.HTML的声明式模版方法;2.JS的可编程能力 JSX的本质 JSX并不是标准…...
GB 38469-2019 船舶涂料中有害物质限量检测
船舶涂料是指涂于船舶各部位,能防止海水、海洋大气腐蚀和海生物附着及满足船舶特种要求的各种涂料的统称。 GB 38469-2019船舶涂料中有害物质限量检测项目: 测试指标 测试方法 挥发性有机化合物VOC GB 30981 甲苯 GB 24408 苯 GB 30981 甲醇 G…...
汇编:数组-寻址取数据
比例因子寻址: 比例因子寻址(也称为比例缩放索引寻址或基址加变址加比例因子寻址)是一种复杂的内存寻址方式,常用于数组和指针操作。它允许通过一个基址寄存器、一个变址寄存器和一个比例因子来计算内存地址。 语法 比例因子寻…...
ROS自带的OpenCV库和自己安装版本冲突问题现象及解决方法
文章目录 1. 问题现象1.1 编译过程警告1.2 程序运行报错 2. 分析问题原因3. 解决方法 1. 问题现象 1.1 编译过程警告 warning: lipopencv_improc.so.406, needed by /usr/local/lib/libopencv_xfeatures2d.so.4.6.0, may conflict with libopencv_imgproc.so.4.21.2 程序运行…...
html+CSS+js部分基础运用19
1. 应用动态props传递数据,输出影片的图片、名称和描述等信息【要求使用props】,效果图如下: 2.在页面中定义一个按钮和一行文本,通过单击按钮实现放大文本的功能。【要求使用$emit()】 代码可以截图或者复制黏贴放置在“实验…...
探索 Debian 常用命令:掌握 Linux 系统管理的重要一步
Debian 作为一个稳定、高效和安全的操作系统,广泛应用于服务器、桌面和嵌入式系统中。对于新手和经验丰富的系统管理员来说,熟练掌握 Debian 的常用命令是管理和维护系统的基础。本文将详细介绍一些在 Debian 系统中经常使用的命令,帮助读者更好地理解和操作这个强大的操作系…...
「C系列」C 作用域规则
文章目录 一、C 作用域规则二、案例1. 块作用域(Block Scope)2. 文件作用域(File Scope)3. 静态作用域(Static Scope)静态局部变量静态全局变量 4. 函数参数的作用域5. 结构体和联合体的作用域 三、相关链接…...
【机器学习基础】Python编程10:五个实用练习题的解析与总结
Python是一种广泛使用的高级编程语言,它在机器学习领域中的重要性主要体现在以下几个方面: 简洁易学:Python语法简洁清晰,易于学习,使得初学者能够快速上手机器学习项目。 丰富的库支持:Python拥有大量的机…...
【设计模式】结构型设计模式之 门面模式
介绍 门面模式(Facade Pattern)是一种常用的设计模式,属于结构型模式的范畴。它为子系统中的一系列接口提供一个简化的统一接口,即一个外观(Facade),从而使子系统更加容易使用。门面模式并不修…...
MAC地址简介
一、MAC和ip地址 很多同学只知道ip地址,同时也知道ip在网络通讯中的重要性,实际上要实现网络通信的话,除了ip地址外还需要MAC地址的配合,只有在这两种地址的配合之下才能完整的实现互联网的通信。但是由于MAC地址的使用࿰…...
五种网络IO模型
目录 前言 文件描述符 为什么要多种io模型 同步IO 1.阻塞IO 2.非阻塞IO 3.多路复用IO(事件驱动IO) select: poll: epoll: 4.信号驱动IO 异步IO 区别 前言 文件描述符 首先我们了解一下文件描述符是什么:…...
VSCode超过390万下载的请求插件
Thunder Client 是一款在 VSCode(Visual Studio Code)中非常受欢迎的 REST API 客户端插件,由Ranga Vadhineni开发,现在已经有超过390万的下载量。它允许开发者直接在编辑器内发送 HTTP 请求,查看响应。Thunder Client…...
前端 JS 经典:下载的流式传输
触发下载在浏览器中有两种方式:1. 客户端的方式 2. 服务器的方式 1. 服务器的方式 通过 a 元素链接到一个服务器的地址,然后需要后端人员配置,当用户点击按钮请求这个地址时,服务端给他加上一个响应头。Content-Disposition 设置…...
wordpress mysql类/短视频营销方式有哪些
明天都儿童节了,要和五月份说再见了。这个月居然一篇博文都没有写,甚是惭愧>_python通常使用pip安装第三方库,如果你安装的库比较多总会遇到一些恼人的安装问题(如果安装了VC2008或许会好很多),所以像我等小白安装一些比较复杂…...
做康复医院网站/怎样能在百度上搜索到自己的店铺
HTML5 Web SQL Web SQL 数据库可以在最新版的 Safari, Chrome 和 Opera 浏览器中工作。 三个核心方法: openDatabase:这个方法使用现有的数据库或者新建的数据库创建一个数据库对象。transaction:这个方法让我们能够控制一个事务ÿ…...
wordpress wp options/数据分析师需要学哪些课程
欢迎大家收看我的视频课程:H5CSS3移动商城界面.七天从零实战课程 黄老师QQ:45157718 ,前端交流群群 147415688 https://edu.csdn.net/course/detail/24836...
佛山 网站建设/站内推广和站外推广的区别
1 例子 故事:老王烧开水。 出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。 老王想了想,有好几种等待方式 1.老王用水壶煮水,并且站在那…...
阿里云ecs建网站/最有效的app推广方式有哪些
转自 http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html...
php动态网站怎么做的/免费seo关键词优化方案
9月12日-9月14日,2016中国(北京)国际大数据产业博览会暨高峰论坛,在北京中国国际展览中心举行。本届展会以"促进大数据时代变革、共赢新时代机遇和挑战"为主题,展示了大数据产业领域的最新成果、新技术,吸引了众多业内人…...