博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义有多个按钮节点的SliderView
阅读量:5756 次
发布时间:2019-06-18

本文共 14070 字,大约阅读时间需要 46 分钟。

###前言

前些天看到一个设计图,关于分期付款选择期数的,有多个节点。它像是一个sliderView,但是sliderView实现不了多个节点按钮。所以,我就想到了自定义sliderView。

设计图如下:

最终效果图如下:

###设计思路

  1. 先添加一个底层view,然后在底层view上画出背景layer,这里是六个小圆,和一个细长矩形。

  2. 小圆点是可点的,所以还要创建六个btn,并添加下标题。

  3. 在底层view的上方添加一个view,充当滑动控制器。

  4. 在滑动控制器上添加拖拽手势,并且控制滑动时,只改变控制器的X坐标,Y轴保持不变。

  5. 绘制绿色layer跟随滑动控制器而动。

  6. 处理各个按钮的点击事件,让滑动控制器跟绿色layer随之改变。

  7. 处理细节,吸附功能,点亮下标题,对滑动控制器最小和最大X轴位移的控制。

  8. 设置代理,在各个方法里触发代理方法。

###实现相关功能

  1. 创建底层view,在view上添加各种layer;创建btn和下标题。

#pragma mark --- 加载所有的layer- (void)drawWholeShape{    CGFloat gapX = self.frame.origin.x; //父视图距离屏幕左边的距离(实现各个圆之间的间距逐渐增大,我自己设置了几个参数,大家可以根据自己的实际情况去改变圆之间的间距。不是非要按照这个来,这里只是提供思路。)    // 用贝塞尔函数画出细长矩形路径    UIBezierPath *recPath = [UIBezierPath bezierPath];    [recPath moveToPoint:CGPointMake(8, 4)];//上起点    [recPath addLineToPoint:CGPointMake(8, 8)];//下起点    [recPath addLineToPoint:CGPointMake(8+WIDTH-2*gapX, 8)];//下结束点    [recPath addLineToPoint:CGPointMake(8+WIDTH-2*gapX, 4)];//上结束点// 用CAShapeLayer绘制细长矩形    CAShapeLayer *tubeShape = [[CAShapeLayer alloc]init];    tubeShape.path = recPath.CGPath;    tubeShape.strokeColor = [UIColor colorWithRed:224/255.0 green:224/255.0 blue:224/255.0 alpha:1].CGColor;// 外边框颜色    tubeShape.fillColor = [UIColor colorWithRed:224/255.0 green:224/255.0 blue:224/255.0 alpha:1].CGColor;// 内部填充颜色     [_holeShapeView.layer addSublayer:tubeShape];        NSArray *title = TITLE;    // for 循环绘制六个灰色小圆跟绿色小圆,创建六个btn,下标题并添加进数组    for (int i = 0; i <6; i ++) {                //灰色小圆        UIBezierPath *leftSemiPath1 = [UIBezierPath bezierPath];                CGPoint pointR1 = CGPointMake(12 +(_yy+_xx*i)*i, 6);                [leftSemiPath1 addArcWithCenter:pointR1 radius:6 startAngle:(0.0 * M_PI) endAngle:(2.0 * M_PI) clockwise:YES];                        CAShapeLayer *leftSemiShape1 = [[CAShapeLayer alloc]init];                leftSemiShape1.path = leftSemiPath1.CGPath;                leftSemiShape1.strokeColor = [UIColor colorWithRed:224/255.0 green:224/255.0 blue:224/255.0 alpha:1].CGColor;        leftSemiShape1.fillColor = [UIColor colorWithRed:224/255.0 green:224/255.0 blue:224/255.0 alpha:1].CGColor;        [_holeShapeView.layer addSublayer:leftSemiShape1];                        // 绿色小圆        UIBezierPath *leftSemiPath2 = [UIBezierPath bezierPath];                CGPoint pointR2 = CGPointMake(12 +(_yy+_xx*i)*i, 6);                [leftSemiPath2 addArcWithCenter:pointR2 radius:4 startAngle:(0.0 * M_PI) endAngle:(2.0 * M_PI) clockwise:YES];                        CAShapeLayer *leftSemiShape2 = [[CAShapeLayer alloc]init];                leftSemiShape2.path = leftSemiPath2.CGPath;                leftSemiShape2.strokeColor = K_CGColor;        leftSemiShape2.fillColor = K_CGColor;        [self.btnLayerArr addObject:leftSemiShape2];                if (i==0) {           // 将第一个绿色小圆添加到底层view上            [_holeShapeView.layer addSublayer:leftSemiShape2];        }                float x = 4 +(_yy+_xx*i)*i;        // 创建btn        UIButton *stepBtn = [[UIButton alloc]initWithFrame:CGRectMake(x, -2, 14, 14)];                [_btnArr addObject:stepBtn];        [self.btnOriginXArr addObject:@(x)];                stepBtn.tag = i;                [stepBtn addTarget:self action:@selector(onBtnClick:) forControlEvents:UIControlEventTouchUpInside];        [self addSubview:stepBtn];                // 创建下标题        UILabel *qiShuLabel = [[UILabel alloc]init];                qiShuLabel.center = CGPointMake(x-4, 20);        qiShuLabel.text = title[i];        qiShuLabel.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];        qiShuLabel.font = [UIFont systemFontOfSize:12];                [qiShuLabel sizeToFit];                [self addSubview:qiShuLabel];                [self.titleLabelArr addObject:qiShuLabel];    }   }复制代码

2 . 创建滑动控制器view,并添加滑动手势。

- (void)initTargetView{    _targetView = [[UIImageView alloc]initWithFrame:CGRectMake(0, -6, 22, 22)];    _targetView.image = [UIImage imageNamed:@"target"];        _targetView.userInteractionEnabled = YES;        UIPanGestureRecognizer *imageViewPanGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGesture:)];        [_targetView addGestureRecognizer:imageViewPanGesture];        [self addSubview:_targetView];}复制代码
//在移动过程中,UIGestureRecognizerStateChanged 这个状态会调用很多次,在这里面处理绿色细长矩形的绘制,添加或删除绿色小圆layer。//在移动结束时,UIGestureRecognizerStateEnded 这个状态只调用一次,在这里处理最终的绿色细长矩形,绿色小圆,下标题的点亮,吸附功能。- (void)panGesture:(UIPanGestureRecognizer *)gesture{    CGFloat y;       switch (gesture.state) {                    case UIGestureRecognizerStateBegan:        {                        CGRect rect = gesture.view.frame;            y = rect.origin.y ;        }            break;        case UIGestureRecognizerStateChanged:        {            // 获得添加手势的对象            // 获得滑动的距离  包含 x y 移动的数值            CGPoint point  =[gesture translationInView:gesture.view];                        CGRect targetRect = _targetView.frame;                        CGFloat targetX = targetRect.origin.x;           // 绿色的细长矩形            [_recPath removeAllPoints];// 这个方法会调用很多次,每次调用都会绘制一条路径,为了实现绿色路径跟随滑动控制器而动的效果,所有每次绘制之前都移除掉所有的点,其它地方有这样的处理都是一个道理。            [_recPath moveToPoint:CGPointMake(8, 5.8)];            [_recPath addLineToPoint:CGPointMake(8, 7)];                        if (targetX>8) {// 避免超出最小范围                [_recPath addLineToPoint:CGPointMake(targetX, 7)];                [_recPath addLineToPoint:CGPointMake(targetX, 5.8)];            }                        [_recPath closePath];                        _tubeShape.path = _recPath.CGPath;            [_tubeShape setNeedsDisplay];            [self.layer addSublayer:_tubeShape];                        NSArray *titleArr = TITLE;                        for (int i = 0; i <6; i ++) {                                if (i!=5) {                    // 滑动过程中添加和删除绿色圆layer                    if (targetX >= [self.btnOriginXArr[i]integerValue] && targetX < [_btnOriginXArr[i+1]integerValue]) {                        // 删除上一个绿色小圆layer                        CAShapeLayer *layer = self.btnLayerArr[i+1];                        if (layer) {                            [layer removeFromSuperlayer];                        }                        // 添加新的绿色小圆layer                        [_holeShapeView.layer addSublayer:self.btnLayerArr[i]];                        [_shapeViewDelegate onShapeViewDelegateEventWithString:titleArr[i]];// 调用代理方法,回调期数                    }                                    }                            }            //CGRectOffset是以试图的原点为起始 移动 dx x移动距离  dy y移动距离                       gesture.view.frame =CGRectOffset(gesture.view.frame, point.x, y );// 改变滑动控制器的frame,只改变X,Y坐标保持不变。                        //清空移动距离            [gesture setTranslation:CGPointZero inView:gesture.view];                                }            break;        case UIGestureRecognizerStateEnded:        {            CGRect targetRect = _targetView.frame;                        CGFloat targetX = targetRect.origin.x;                        float btnX = [self.btnOriginXArr.lastObject integerValue];           // targetView在第一个圆            if (targetX<0) {                                targetRect.origin.x = 0;                                _targetView.frame = targetRect;                                [_shapeViewDelegate onShapeViewDelegateEventWithString:@"1期"];                // 改变下标题颜色                for (UILabel *label in self.titleLabelArr) {                                        label.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];                }                UILabel *firstLabel = self.titleLabelArr.firstObject;                                                firstLabel.textColor = [UIColor colorWithCGColor:K_CGColor];                break;            }            // targetView在最后一个圆            if (targetX >btnX) {                                targetRect.origin.x = btnX;                                _targetView.frame = targetRect;                [_shapeViewDelegate onShapeViewDelegateEventWithString:@"12期"];                // 改变下标题颜色                for (UILabel *label in self.titleLabelArr) {                                        label.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];                }                UILabel *firstLabel = self.titleLabelArr.lastObject;                                                firstLabel.textColor = [UIColor colorWithCGColor:K_CGColor];                break;            }                        NSArray *titleArr = TITLE;            // targetView 在中间各个圆            for (int i = 0; i <6; i ++) {                                if (i!=5) {                                       if (targetX >= [self.btnOriginXArr[i]integerValue] && targetX < [_btnOriginXArr[i]integerValue]+15.0 +_middleGap*i) {                                                NSLog(@"%ld",(long)[_btnOriginXArr[i]integerValue]);                                                targetRect.origin.x = [_btnOriginXArr[i]integerValue];                        _targetView.frame = targetRect;                                                [_shapeViewDelegate onShapeViewDelegateEventWithString:titleArr[i]];                                                for (UILabel *label in self.titleLabelArr) {                                                        label.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];                        }                        UILabel *firstLabel = self.titleLabelArr[i];                                                firstLabel.textColor = [UIColor colorWithCGColor:K_CGColor];                                            }                    else if(targetX >=[_btnOriginXArr[i]integerValue]+10.0 + _middleGap*i)                    {                        targetRect.origin.x = [_btnOriginXArr[i+1]integerValue];                        _targetView.frame = targetRect;                        [_shapeViewDelegate onShapeViewDelegateEventWithString:titleArr[i+1]];                                                // 改变下标题颜色                        for (UILabel *label in self.titleLabelArr) {                                                        label.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];                        }                        UILabel *firstLabel = self.titleLabelArr[i+1];                        firstLabel.textColor = [UIColor colorWithCGColor:K_CGColor];                    }                  }            }           // 先移除贝塞尔所有的点,然后重新绘制贝塞尔路径            [_recPath removeAllPoints];            [_recPath moveToPoint:CGPointMake(8, 5.8)];            [_recPath addLineToPoint:CGPointMake(8, 7)];                        [_recPath addLineToPoint:CGPointMake(_targetView.frame.origin.x, 7)];            [_recPath addLineToPoint:CGPointMake(_targetView.frame.origin.x, 5.8)];                        [_recPath closePath];            _tubeShape.path = _recPath.CGPath;            [_tubeShape setNeedsDisplay];            [self.layer addSublayer:_tubeShape];        }                  break;        default:            break;    }}复制代码

3 . 处理按钮的点击事件。

- (void)onBtnClick:(UIButton *)btn{    NSArray *titleArr = TITLE;        [_shapeViewDelegate onShapeViewDelegateEventWithString:titleArr[btn.tag]];// 回调代理    // 滑动控制器frame动画    [UIView animateWithDuration:0.3 animations:^{                NSInteger x   = [_btnOriginXArr[btn.tag]integerValue];        CGRect rect   = _targetView.frame;        rect.origin.x = x;        _targetView.frame = rect;           } completion:^(BOOL finished) {        // 改变下标题颜色        for (UILabel *label in self.titleLabelArr) {                        label.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];        }        UILabel *firstLabel = self.titleLabelArr[btn.tag];                firstLabel.textColor = [UIColor colorWithCGColor:K_CGColor];    }];// layer的动画没处理好,这里通过延迟处理,实现相关功能,下次layer动画处理好了再补充上来。    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        //绿色小圆layer的添加和删除        for (CAShapeLayer *layer in self.btnLayerArr) {            [layer removeFromSuperlayer];        }        for (int i = 0; i < btn.tag+1; i ++) {                                    [_holeShapeView.layer addSublayer:self.btnLayerArr[i]];                    }        // 先移除贝塞尔所有的点,然后重新绘制贝塞尔路径        [_recPath removeAllPoints];        [_recPath moveToPoint:CGPointMake(8, 5.8)];        [_recPath addLineToPoint:CGPointMake(8, 7)];                if (_targetView.frame.origin.x > 8) {// 控制最小距离                        [_recPath addLineToPoint:CGPointMake(_targetView.frame.origin.x, 7)];            [_recPath addLineToPoint:CGPointMake(_targetView.frame.origin.x, 5.8)];                    }                [_recPath closePath];                _tubeShape.path = _recPath.CGPath;        [_tubeShape setNeedsDisplay];        [self.layer addSublayer:_tubeShape];                    });}复制代码

###具体使用方法 下载好我的,在工程中导入DCSliderView类,设置代理ShapeViewDelegate,具体代码如下:

// 1.    DCSliderView *shapeView = [[DCSliderView alloc]initWithFrame:CGRectMake(10, 60, self.view.frame.size.width -20, 30) WithLayerColor:[UIColor colorWithRed:0/255.0 green:210/255.0 blue:87/255.0 alpha:1]];    // DCSliderView 的左右间距10 ,宽度self.view.frame.size.width -20,最好不要变。    // 2.    shapeView.shapeViewDelegate = self;        //3.    [self.view addSubview:shapeView];    _qiShuLabel = [[UILabel alloc]init];        _qiShuLabel.center = CGPointMake(self.view.frame.size.width/2-30, 160);        _qiShuLabel.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1];        _qiShuLabel.font = [UIFont systemFontOfSize:14];        _qiShuLabel.text = @"1期" ;    [_qiShuLabel sizeToFit];        [self.view addSubview:_qiShuLabel];// 4.代理方法- (void)onShapeViewDelegateEventWithString:(NSString *)str{    _qiShuLabel.text = str ;    [_qiShuLabel sizeToFit];    }复制代码

######需要注意的一点是,由于各个小圆之间的间距是逐渐增大的,所以我根据屏幕的宽度设置了几个不同的系数去适配,如果你没有使用我代码中的宽度,适配就会出现问题。其实本文只是一个引子,主讲设计思路,你可以按照自己的实际情况去具体设计。当然,如果你不想动手修改的话,那就得按照我设计的来。


转载请注明出处 © XDChang

你可能感兴趣的文章
【URAL】1297 Palindrome【字符串--manacher算法】
查看>>
c#中的interface abstract与virtual
查看>>
Bootstrap定制(一)less入门及编译
查看>>
java基础篇 -- 常用的日期加减和日期格式化工具类
查看>>
no identity found Command /usr/bin/codesign failed with exit code 1 报错解决方法
查看>>
Windows下HG服务器的搭建
查看>>
laravel的日志服务
查看>>
微信小程序中对于变量的定义
查看>>
DP专题学(复)习笔记
查看>>
20060309: 烟
查看>>
SQL JOIN--Nested Join
查看>>
商品的包装(外贸知识二)
查看>>
Django--中间件
查看>>
HDU 1069 基础动态规划+排序
查看>>
Azure Messaging
查看>>
OptionsMenu创建的两种方式
查看>>
数组转DataTable
查看>>
WinForm搭载ScintillaNET时文本由于发生偏移被隐藏解决方案
查看>>
codeforces 830 B Cards Sorting
查看>>
介绍 JSON
查看>>