zl程序教程

您现在的位置是:首页 >  其他

当前栏目

UISlider实现整数滑动,点击响应,大小高度样式定制

2023-04-18 14:52:01 时间

经常会有人认为UISlider非常鸡肋,只能实现简单的滑动条效果,不能定制样式,不能点击某个位置跳转等等,事实上UISlider的扩展性很强.

一.定制样式

1.取值范围 slider的值并不是必须在0到1之间,是可以随便设置的,其实多数场景下设置整数更方便.

@property(nonatomic) float minimumValue;            
@property(nonatomic) float maximumValue;   

比如有10种等级,就可以设置为1到10级,minimumValue=1;maximumValue=10;

2.整数滑动 slider的value是float型,滑动的时候value会平滑的过渡,如果设置了1到10的范围,我们可能就不需要这些中间的小数

- (void)sliderValueChange:(UISlider *)slider{
    NSInteger index = round(slider.value);
    slider.value = index;
}

可以用这种方式让slider在整数之间跳跃,round是四舍五入为整数,实现跳跃滑动的效果

3.颜色和图片 UISlider可以给滑块以及滑块两边的轨道分别设置颜色和图片,另外在滑动条两边还可以分别设置一个图片 这个部分没什么特别的,试一下就知道;

//坐标轨道的颜色
@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的结构是轨道背景色+轨道图片+滑块背景色+滑块图片

结构

// 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改变的时候调用这些方法,

- (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会调用这个方法,如果设置不当,滑块就会在被点击的时候移动,因此这里先获取了父类的结果,再进行修改

如果想改变滑动条的方向,还可以加个变换

slider.transform = CGAffineTransformRotate(slider.transform, -M_PI_2);

比如改成垂直的,由于仿射变换改变了坐标系,所以其他代码全都不需要改变

变换

二.点击轨道响应值变化

- (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