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实现获取汉字的首字母
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...
- 【收藏】socket 中的 recv与send函数
收藏自世道:http://www.cnblogs.com/jianqiang2010/archive/2010/08/20/1804598.html 1.send 函数 int send( SOCKE ...
- 表连接查询的顺序和where子句条件的前后顺序会影响sql的性能么
有好多时候,我们常听别人说大表在前,小表在后,包括现在好多百度出来的靠前的答案都有说数据库是从右到左加载的,所以from语句最后关联的那张表会先被处理.如果三表交叉,就选择交叉表来作为基础表.等等一些 ...
- 关于并发,关于IIS你真的了解吗?(一)
本文仅代表带个人观点及理解,本人只是一个编程小菜鸟,如果有不对的地方.请大佬轻喷! 前言:对于很多工作时间短或者编程经验不足的程序员来说,大多数会觉得并发这个词离自己太遥远,之所以知道并发也不过是因为 ...
- .Net中关于等于的故事(一)
在.Net框架中,如果您查看所有类型的的基类:System.Object类,将找到如下4个与相等判断的方法: static Equals() virtual Equals() static Refer ...
- 用netsh wlan命令行解决“Win10下WLAN不自动登陆”问题
系统崩溃了,找了一个版本Windows 10重装后,发现进入系统后不会自动连接自己家的Wifi,每次都要手动点"登录",烦不胜烦. 于是百度.Google一起上,找解决方案,然后所 ...
- PHP加密解密数字
<?php /** * 加密解密类,PHP加密解密数字,适用于URL加密 * 该算法仅支持加密数字.比较适用于数据库中id字段的加密解密,以及根据数字显示url的加密. * @version a ...
- VS2015 +Qt5 串口工具
简单的小工具是VS2015 + Qt5.6.1实现的,界面部分是Qt实现,串口是封装的WinAPI,把串口收发模块封装成了个Serialport.dll 供Qt界面调用. 由于VS2015需要CRT运 ...
- 详解Mysql自动备份与恢复
通过 mysqldump命令,直接生成一个完整的 .sql 文件 Step 1: 创建一个批处理备份SQL c: cd C:Program Filesmysql5.6.24bin mysqldump ...
- Java基础语法<七> 对象与类
笔记整理 来源于<Java核心技术卷 I > <Java编程思想> 1. 类之间的关系 依赖 users– a 是一种最明显的.最常见的关系.如果一个类的方法操作另一个类的对象 ...