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> ...
随机推荐
- http服务器开发笔记(一)——先跑起来
做了很多年的web相关开发,从来也没有系统的学习http协议,最近正好工作不怎么忙,准备系统的学习一下. 接下来准备自己写一小型的http服务器来学习,因为现在对JavaScript比较熟悉,所以决定 ...
- CSS动画属性性能详细介绍
CSS动画属性会触发整个页面的重排relayout.重绘repaint.重组recomposite Paint通常是其中最花费性能的,尽可能避免使用触发paint的CSS动画属性,这也是为什么我们推荐 ...
- [HNOI2007]紧急疏散EVACUATE (湖南2007年省选)
[HNOI2007]紧急疏散EVACUATE 题目描述 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面 ...
- 基于node.js制作爬虫教程
前言:最近想学习node.js,突然在网上看到基于node的爬虫制作教程,所以简单学习了一下,把这篇文章分享给同样初学node.js的朋友. 目标:爬取 http://tweixin.yueyishu ...
- laravel如何向视图传递变量的方法
第一种: public function about(){ $name = "cai" return view("sites.about")->with( ...
- Object-C知识点 (三) 单例 蒙版 刷新 KVO底层
#pragma mark - 单例方法(完整的方法) 系统的单例方法名称 sharedApplication defaultManager standardUserDefaults currentDe ...
- Linux之正则表达式
正则表达式与通配符的区别: 最常应用正则表达式的命令是grep(egrep),sed,awk. 正则表达式和通配符有本质区别,正则表达式用来找:[文件]内容,文本,字符串.一般只有三剑客支持.通配符用 ...
- RabbitMQ安装|使用|概念|Golang开发
搬砖的陈大师版权所有,转载请注明:http://www.lenggirl.com/tool/RabbitMQ.html 手册:http://www.rabbitmq.com/getstarted.ht ...
- twemproxyMemcache协议解析探索——剖析twemproxy代码正编补充
memcache是一种和redis类似的高速缓存服务器,但是memcache只提供键值对这种简单的存储方式,相对于redis支持的存储方式多样化,memcache就比较简单了.memcache通过tc ...
- [UWP]浅谈按钮设计
一时兴起想谈谈UWP按钮的设计. 按钮是UI中最重要的元素之一,可能也是用得最多的交互元素.好的按钮设计可以有效提高用户体验,构造让人眼前一亮的UI.而且按钮通常不会影响布局,小小的按钮无论怎么改也不 ...