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> ...
随机推荐
- 深入探索C++对象模型(六)
执行期语意学(Runtime Semantics) 对象的构造和析构(Object Constructor and Destructor) 一般而言,constructor和destructor的安插 ...
- (转) Unicode(UTF-8, UTF-16)令人混淆的概念
原文地址:http://www.cnblogs.com/kingcat/archive/2012/10/16/2726334.html 为啥需要Unicode 我们知道计算机其实挺笨的,它只认识010 ...
- React + Redux + express+ antd 架构的认识
在过去的两周里,我使用这套技术栈进行项目页面的开发.下面是我个人的对于项目的一些看法: 首先:是项目的调试,我深表压力很大,项目是使用fibber去抓包调试的,也不知道我们项目的负责人,怎么能的我在每 ...
- [USACO08JAN]手机网络Cell Phone Network
[USACO08JAN]手机网络Cell Phone Network 题目描述 Farmer John has decided to give each of his cows a cell phon ...
- Web 前端代码规范
Web 前端代码规范 最后更新时间:2017-06-25 原始文章链接:https://github.com/bxm0927/web-code-standards 此项目用于记录规范的.高可维护性的前 ...
- 论文笔记 Generative Face Completion
这篇paper将巧妙地将四个loss函数结合在一起,其中每一个loss的功能不同.但这篇paper不够elegant的地方也是loss太多!在本文中,我采用散文的写作方法谈谈自己对这篇paper的理解 ...
- jquery按钮倒计时
<html> <head> <script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"> ...
- thinkphp中fetch渲染模板的处理
<script type="text/javascript"> function xiugai(elm){ var formData1=$("#a1_&quo ...
- 给linux虚拟机添加Samba用户
Window系统连上我们的开发机Linux,自然需要在Samba里添加一个新用户. linux-06bq:/usr/local/services/samba/bin # ./smbpasswd -a ...
- iOS10 相关的隐私设置,
最近下载了几个demo,总是一运行就崩,看了下崩溃日志,有几个是因为没在plist里设置因此权限,而现在iOS 10 开始对隐私权限更加严格, 如需使用隐私权限需要在工程的info.plist文件中声 ...