Objective-C 自定义UISlider滑杆 分段样式
效果
- 自定义一个功能简单的分段的滑杆 可显示分段名
- 为了显示效果,我们将滑块和节点都设置为不规则
- 这里只实现了分段的slider,未分段的没有实现,有兴趣的可以定义另一种类型做个判断修改下
需求分析
我们需要的是一个可分段slider
主要元素有
- 滑杆视图
- 滑杆上的节点
- 节点名称
- 当前滑块
由于要设定节点和滑块的frame,size又是可变的,最死脑细胞的就是滑块和节点frame的计算了!!!!
代码部分
查看UISlider控件发现这个类是继承自UIControl,那么就自定义一个UIControl来实现UISlider没有的效果。
公开的变量中都是一些通用的参数,无非就是颜色、图片之类的,我这里使用比较少,有兴趣的可以自行添加。
- 公开变量
#import <UIKit/UIKit.h>
@interface CustomSlider : UIControl
//当前值
@property (nonatomic,assign)NSInteger value;
//选择点个数 >2
@property (nonatomic,assign)NSInteger numberOfPart;
//当前滑块图片
@property (nonatomic,strong)UIImage *thumbImage;
//当前滑块尺寸
@property (nonatomic,assign)CGSize thumbSize;
//分段点尺寸
@property (nonatomic,assign)CGSize partSize;
//slider height
@property (nonatomic,assign)CGFloat sliderBarHeight;
//分段名
@property (nonatomic,strong)NSArray<NSString*> *partNameArray;
//分段名偏移位置 初始在slider上
@property (nonatomic,assign)CGPoint partNameOffset;
@end
- 设置非公开的全局变量
@interface CustomSlider()
@property(nonatomic,assign)CGRect thumbRect; //滑块fram
@property(nonatomic,assign)CGRect sliderRect; //sliderFrame
@property(nonatomic,strong)NSMutableArray *partRectArray; //节点frame数组
@property(nonatomic,assign)BOOL isFirst; //首次运行
@property(nonatomic,strong)NSDictionary *textAttributesDict;//文本字体信息
@end
- 一些初始化
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.partRectArray = [NSMutableArray array];
self.numberOfPart = 2;
self.sliderBarHeight = 20;
self.thumbSize = CGSizeMake(self.sliderBarHeight, self.sliderBarHeight);
self.partNameOffset = CGPointZero;
self.partSize = self.thumbSize;
}
return self;
}
- 设置一个set方法
//节点必须>1
- (void)setNumberOfPart:(NSInteger)numberOfPart {
_numberOfPart = numberOfPart > 1 ? numberOfPart: 2;
}
- 设置文本属性,用于绘制文字
//设置文本属性
- (NSDictionary*)textAttributesDict {
if (!_textAttributesDict) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSForegroundColorAttributeName] = [UIColor blackColor]; // 文字颜色
dict[NSFontAttributeName] = [UIFont systemFontOfSize:14]; // 字体
_textAttributesDict = dict;
}
return _textAttributesDict;
}
- 重绘主要是画带节点的slider和滑块
//重绘
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
//画slider
[self loadSliderWithContext:context];
//画滑块
[self loadThumWithContext:context];
}
//画滑块
- (void)loadThumWithContext:(CGContextRef)context {
//当前滑块
if (!self.isFirst) {
//滑块初始化
self.thumbRect = [self.partRectArray[0] CGRectValue];
self.isFirst = YES;
}
//有背景图则用背景图 没有就画圆
if (self.thumbImage) {
[self.thumbImage drawInRect: self.thumbRect];
} else {
CGContextAddEllipseInRect(context, self.thumbRect);
[[UIColor blueColor] set];
CGContextFillPath(context);
}
}
//画滑杆内容
- (void)loadSliderWithContext:(CGContextRef)context {
//设置滑杆的frame
CGFloat tmp = self.thumbSize.width > self.partSize.width ? self.thumbSize.width : self.partSize.width;
CGFloat tmpHeight = self.thumbSize.height > self.partSize.height ? self.thumbSize.height : self.partSize.height;
CGFloat sliderHeight = self.sliderBarHeight;
CGFloat sliderMargin = sliderHeight/2 + tmp/2;
CGFloat sliderY = CGRectGetHeight(self.frame)/2;
CGFloat sliderWidth = CGRectGetWidth(self.frame) - 2 * sliderMargin - tmp;
self.sliderRect = CGRectMake(sliderMargin + self.partSize.width/2, sliderY-tmpHeight/2, sliderWidth+tmp, tmpHeight);
//画slider滑杆
CGPoint starPoint = CGPointMake(sliderMargin + tmp/2, sliderY);
CGPoint endPoint = CGPointMake(CGRectGetWidth(self.frame)-sliderMargin- tmp/2, sliderY);
CGContextMoveToPoint(context, starPoint.x, starPoint.y);
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
CGContextSetLineWidth(context, sliderHeight); // 线的宽度
CGContextSetLineCap(context, kCGLineCapRound); // 起点和重点圆角
CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
CGContextStrokePath(context);
//画分段点
CGFloat partPointWidth = self.partSize.width;
CGFloat partPointHeight = self.partSize.height;
CGFloat y = CGRectGetHeight(self.frame)/2 - self.partSize.height/2;
[self.partRectArray removeAllObjects];
for (int i = 0; i < self.numberOfPart; i++) {
CGFloat x = i * sliderWidth/(self.numberOfPart-1) + starPoint.x-partPointWidth/2 ;
CGContextAddEllipseInRect(context, CGRectMake(x, y, partPointWidth, partPointHeight));
[[UIColor blackColor] set];
CGContextFillPath(context);
//计算滑块位置
CGRect fram = CGRectMake(x+partPointWidth/2-self.thumbSize.width/2, y+partPointHeight/2-self.thumbSize.height/2, self.thumbSize.width, self.thumbSize.height);
[self.partRectArray addObject:[NSValue valueWithCGRect:fram]];
}
//画分段点名称
for (int i = 0; i < self.partNameArray.count; i++) {
NSString *partName = self.partNameArray[i];
CGRect parRect = [self.partRectArray[i] CGRectValue];
//根据字体属性 计算出size
CGSize size = [partName sizeWithAttributes:self.textAttributesDict];
CGFloat nameX = parRect.origin.x + parRect.size.width/2 - size.width/2 + self.partNameOffset.x;
CGFloat nameY = parRect.origin.y + self.partNameOffset.y;
CGRect nameFrame = CGRectMake(nameX, nameY, size.width, size.height) ;
[partName drawInRect:nameFrame withAttributes:self.textAttributesDict];
}
}
- 最后设置拖动滑块和滑块最后停留的位置
//开始拖动
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event {
CGPoint point = [touch locationInView:self];
//可滑动范围
if (!CGRectContainsPoint(self.sliderRect, point)) {
return NO;
}
int index = [self getCurrentXWithOffset:point.x];
self.thumbRect = [self.partRectArray[index] CGRectValue];
[self setNeedsDisplay];
return YES;
}
//持续拖动
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event {
CGPoint point = [touch locationInView:self];
CGFloat thumbImageX = point.x - self.thumbSize.width/2;
self.thumbRect = CGRectMake(thumbImageX, self.thumbRect.origin.y, self.thumbRect.size.width, self.thumbRect.size.height);
[self setNeedsDisplay];
return YES;
}
//拖动结束
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event {
CGPoint point = [touch locationInView:self];
int index = [self getCurrentXWithOffset:point.x];
self.thumbRect = [self.partRectArray[index] CGRectValue];
[self setNeedsDisplay];
NSLog(@"point = %f",point.x);
//增加控制事件
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
//计算滑到一半时根据占比选择对应位置
- (int)getCurrentXWithOffset:(CGFloat)x {
CGFloat tmpX = x - [self.partRectArray[0] CGRectValue].origin.x;
//超出边界
if (tmpX > [self.partRectArray[self.numberOfPart-1] CGRectValue].origin.x) {
tmpX = [self.partRectArray[self.numberOfPart-1] CGRectValue].origin.x;
}
//计算总长度
CGFloat firstx = [[self.partRectArray firstObject] CGRectValue].origin.x;
CGFloat lastx = [[self.partRectArray lastObject] CGRectValue].origin.x;
CGFloat wid = lastx - firstx;
CGFloat part = wid/(self.numberOfPart-1) ;
//减去滑块占比
CGFloat per = self.thumbSize.width/2/part;
int count = tmpX / part - per + 0.5;
self.value = count;
return count;
}
写的思路比较简单,就是计算frame很伤。
滑块可以设定为图片。
当然slider背景也可以设置成图片,这个我没写进去,相信也是很好修改的。
DEMO地址
https://github.com/gongxiaokai/CustomSliderDemo
Objective-C 自定义UISlider滑杆 分段样式的更多相关文章
- [Swift通天遁地]九、拔剑吧-(3)创建多种自定义Segment分段样式的控件
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- 自定义UISlider的样式和滑块
又做完一个项目,遂到了写帖时间了,要跟大家分享的是:自定义UISlider的图片和滑块,先看一下效果: 然后是实现了,我们使用的是UISlider的setMinimumTrackImage,和setM ...
- arcgis api for js共享干货系列之二自定义Navigation控件样式风格
arcgis api for js默认的Navigation控件样式风格如下图: 这样的风格不能说不好,各有各的爱好,审美观,这里也不是重点,这里的重点是如何自定义一套自己喜欢的样式风格呢:自己自定义 ...
- jquery美化select,自定义下拉框样式
select默认的样式比较丑,有些应用需要美化select,在网上找到一个很好的美化样式效果,本人很喜欢,在这里分享一下. <!DOCTYPE html PUBLIC "-//W3C/ ...
- WPF界面设计技巧(2)—自定义漂亮的按钮样式
原文:WPF界面设计技巧(2)-自定义漂亮的按钮样式 上次做了个很酷的不规则窗体,这次我们来弄点好看的按钮出来,此次将采用纯代码来设计按钮样式,不需要 Microsoft Expression Des ...
- 自定义浏览器滚动条的样式,打造属于你的滚动条风格——兼容IE和webkit(ff不支持)
前段时间,到网上找素材时,看到了一个很个性的滚动条式,打开Chrome的调试工具看了一下,发现不是用JavaScript来模拟实现的,觉得 有必要折腾一下.于是在各大浏览器中对比了一下,发现只用Chr ...
- 自定义checkbox, radio样式
17.2.25.nimil 今天开始做百度前端学院的任务,第一个是自定义checkbox, radio样式. checkbox和radio两个标签是不可以改变样式的,background-color. ...
- arcgis api 3.x for js 共享干货系列之二自定义 Navigation 控件样式风格(附源码下载)
0.内容概览 自定义 Navigation 控件样式风格 源码下载 1.内容讲解 arcgis api 3.x for js 默认的Navigation控件样式风格如下图:这样的风格不能说不好,各有各 ...
- 自定义单选框radio样式
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
随机推荐
- 【PHP】最详细PHP从入门到精通(三)——PHP中的数组
PHP从入门到精通 之PHP中的数组 各位开发者朋友大家好,链接上次更新,我们PHP的学习也更深了一层,本次博主给大家带来PHP数组的数组实例详解的相关资料.数组分为数组数值数值,关联数组,多维数组 ...
- nginx四层负载均衡配置
nginx四层负载均衡配置代理Mysql集群 环境如下: ip 192.168.6.203 Nginx ip 192.168.6.*(多台) Mysql 步骤一 查看Nginx是否安装stream模块 ...
- Redis数据类型之Set
前言:set类似于数学上面的集合概念,包含的元素无序,不能重复,能进行交.并.差操作. 一.内部原理 set数据结构,也是随着元素数目的多少而变化.当set中添加 ...
- WCF学习——构建一个简单的WCF应用(二)
我们接着上一篇文章进行讲解 http://www.cnblogs.com/songjianhui/p/7060698.html 一:客户端通过添加引用调用服务 WCF应用服务被成功寄宿后,WCF服务应 ...
- Discuz开发帮助
http://ishare.iask.sina.com.cn/f/33819255.html Discuz二次开发手册(下载) http://dev.discuz.org/wiki/index.php ...
- 如何自定义容器网络?- 每天5分钟玩转 Docker 容器技术(33)
除了 none, host, bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络. Docker 提供三种 user-defined 网络驱动:bridge ...
- 《JavaScript高级程序设计》笔记一
第一章 JavaScript简介 一.JavaScript的起源 JavaScript诞生于1995年.当时,它的主要作用是处理一些输入验证操作.之前的话,都是把表单数据发送到服务器端,然后再去判断有 ...
- MySQL错误2003:Can't connect to MySQL server (10060)
1.网络不通. 检查能不能ping通. 2.防火墙设置. 防火墙是否放过mysql的进程,是否屏蔽了mysql的3306端口. 3.mysql的账户设置. mysql账户是否不允许远程连接.如果无法连 ...
- linux下安装apache最常见的报错解决
报错如下: Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name, ...
- 【Android Developers Training】 39. 获取文件信息
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...