经常会有人认为UISlider非常鸡肋,只能实现简单的滑动条效果,不能定制样式,不能点击某个位置跳转等等,事实上UISlider的扩展性很强.
一.定制样式
1.取值范围 slider的值并不是必须在0到1之间,是可以随便设置的,其实多数场景下设置整数更方便.
代码语言:javascript复制@property(nonatomic) float minimumValue;
@property(nonatomic) float maximumValue;
比如有10种等级,就可以设置为1到10级,minimumValue=1;maximumValue=10;
2.整数滑动 slider的value是float型,滑动的时候value会平滑的过渡,如果设置了1到10的范围,我们可能就不需要这些中间的小数
代码语言:javascript复制- (void)sliderValueChange:(UISlider *)slider{
NSInteger index = round(slider.value);
slider.value = index;
}
可以用这种方式让slider在整数之间跳跃,round是四舍五入为整数,实现跳跃滑动的效果
3.颜色和图片 UISlider可以给滑块以及滑块两边的轨道分别设置颜色和图片,另外在滑动条两边还可以分别设置一个图片 这个部分没什么特别的,试一下就知道;
代码语言:javascript复制//坐标轨道的颜色
@property(nullable, nonatomic,strong) UIColor *minimumTrackTintColor;
//右边轨道的颜色
@property(nullable, nonatomic,strong) UIColor *maximumTrackTintColor;
//滑块的颜色
@property(nullable, nonatomic,strong) UIColor *thumbTintColor ;
//滑块图片
- (void)setThumbImage:(nullable UIImage *)image forState:(UIControlState)state;
//左边轨道图片
- (void)setMinimumTrackImage:(nullable UIImage *)image forState:(UIControlState)state;
//右边轨道图片
- (void)setMaximumTrackImage:(nullable UIImage *)image forState:(UIControlState)state;
//左边的图片
@property(nullable, nonatomic,strong) UIImage *minimumValueImage;
//右边的图片
@property(nullable, nonatomic,strong) UIImage *maximumValueImage;
滑块图片显示模式是UIViewContentModeScalAspectFill,并且没有clipsToBounds,所以图片的大小很重要 轨道图片是通过resizableImage进行拉伸的 minimumValueImage和maximumValueImage就是左右两个图片而已,因为是始终显示的,所以没什么大用
4.大小和高度 UISlider的结构是轨道背景色 轨道图片 滑块背景色 滑块图片
结构
代码语言:javascript复制// lets a subclass lay out the track and thumb as needed
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds;
- (CGRect)maximumValueImageRectForBounds:(CGRect)bounds;
- (CGRect)trackRectForBounds:(CGRect)bounds;
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value;
滑块的大小和轨道的高度需要在子类中重写,我们看到需要返回的是CGRect,value改变的时候轨道大小和滑块位置自然是在变化的,也就是说UISlider是会在value改变的时候调用这些方法,
代码语言:javascript复制- (CGRect)trackRectForBounds:(CGRect)bounds{
return CGRectMake(0, (bounds.size.height - 5)/2.0, bounds.size.width, 5);
}
需要修改轨道高度的时候用这个就可以了, 这个方法会影响UISlider的默认高度,以往UISlider不需要设置高度,因为设置了也没用,会有最小值,实现这个方法就可以和高度设置结合起来了
对应minimumValueImage的大小
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds; 对应maximumValueImage的大小
- (CGRect)maximumValueImageRectForBounds:(CGRect)bounds;
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value{ CGRect r = [super thumbRectForBounds:bounds trackRect:rect value:value]; CGSize s = r.size; CGFloat wh = 15; return CGRectMake(r.origin.x (s.width - wh)/2, r.origin.y (s.height - wh)/2, wh, wh); }
上面这个是设置滑块的大小和位置,这个方法只对设置了图片的滑块起作用,需要注意的是,value改变后UISlider会调用这个方法,如果设置不当,滑块就会在被点击的时候移动,因此这里先获取了父类的结果,再进行修改
如果想改变滑动条的方向,还可以加个变换
代码语言:javascript复制slider.transform = CGAffineTransformRotate(slider.transform, -M_PI_2);
比如改成垂直的,由于仿射变换改变了坐标系,所以其他代码全都不需要改变
变换
二.点击轨道响应值变化
代码语言:javascript复制- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CGRect t = [self trackRectForBounds: [self bounds]];
CGRect t2 = [self thumbRectForBounds:self.bounds trackRect:t value:self.value];
CGPoint p = [[touches anyObject] locationInView: self];
if(!CGRectContainsPoint(t2, p)){
float v = [self minimumValue] (p.x - t.origin.x) * (([self maximumValue]-[self minimumValue]) / (t.size.width));
[self setValue: v];
id target = self.allTargets.allObjects.firstObject;
NSString *sel = [self actionsForTarget:target forControlEvent:UIControlEventValueChanged].firstObject;
if(sel){
[self sendAction:NSSelectorFromString(sel) to:target forEvent:event];
}
NSString *sel2 = [self actionsForTarget:target forControlEvent:UIControlEventTouchUpInside].firstObject;
if(sel2){
[self sendAction:NSSelectorFromString(sel2) to:target forEvent:event];
}
}else{
[super touchesBegan: touches withEvent: event];
}
}
1.重写touchesBegan方法,计算点击位置的value 2.获取滑块的frame,判断点击是否在滑块内 3.如果是,则不作处理,让父类处理 4.如果不是,则赋值新的value,从target获取选择器,然后sendAction