自定義有多個(gè)按鈕節(jié)點(diǎn)的SliderView

前言

前些天看到一個(gè)設(shè)計(jì)圖岭埠,關(guān)于分期付款選擇期數(shù)的脆丁,有多個(gè)節(jié)點(diǎn)。它像是一個(gè)sliderView,但是sliderView實(shí)現(xiàn)不了多個(gè)節(jié)點(diǎn)按鈕稚叹。所以端礼,我就想到了自定義sliderView。DCSliderView

設(shè)計(jì)圖如下:

期數(shù)設(shè)計(jì)圖.png

最終效果圖如下:

DCSliderView.gif

設(shè)計(jì)思路

  1. 先添加一個(gè)底層view入录,然后在底層view上畫出背景l(fā)ayer,這里是六個(gè)小圓蛤奥,和一個(gè)細(xì)長矩形。

  2. 小圓點(diǎn)是可點(diǎn)的僚稿,所以還要?jiǎng)?chuàng)建六個(gè)btn,并添加下標(biāo)題凡桥。

  3. 在底層view的上方添加一個(gè)view,充當(dāng)滑動(dòng)控制器蚀同。

  4. 在滑動(dòng)控制器上添加拖拽手勢缅刽,并且控制滑動(dòng)時(shí),只改變控制器的X坐標(biāo)蠢络,Y軸保持不變衰猛。

  5. 繪制綠色layer跟隨滑動(dòng)控制器而動(dòng)。

  6. 處理各個(gè)按鈕的點(diǎn)擊事件刹孔,讓滑動(dòng)控制器跟綠色layer隨之改變啡省。

  7. 處理細(xì)節(jié),吸附功能髓霞,點(diǎn)亮下標(biāo)題卦睹,對滑動(dòng)控制器最小和最大X軸位移的控制。

  8. 設(shè)置代理方库,在各個(gè)方法里觸發(fā)代理方法结序。

實(shí)現(xiàn)相關(guān)功能

  1. 創(chuàng)建底層view,在view上添加各種layer纵潦;創(chuàng)建btn和下標(biāo)題徐鹤。
#pragma mark --- 加載所有的layer
- (void)drawWholeShape
{
    CGFloat gapX = self.frame.origin.x; //父視圖距離屏幕左邊的距離(實(shí)現(xiàn)各個(gè)圓之間的間距逐漸增大,我自己設(shè)置了幾個(gè)參數(shù)邀层,大家可以根據(jù)自己的實(shí)際情況去改變圓之間的間距返敬。不是非要按照這個(gè)來,這里只是提供思路被济。)
    // 用貝塞爾函數(shù)畫出細(xì)長矩形路徑
    UIBezierPath *recPath = [UIBezierPath bezierPath];
    [recPath moveToPoint:CGPointMake(8, 4)];//上起點(diǎn)
    [recPath addLineToPoint:CGPointMake(8, 8)];//下起點(diǎn)
    [recPath addLineToPoint:CGPointMake(8+WIDTH-2*gapX, 8)];//下結(jié)束點(diǎn)
    [recPath addLineToPoint:CGPointMake(8+WIDTH-2*gapX, 4)];//上結(jié)束點(diǎn)
// 用CAShapeLayer繪制細(xì)長矩形
    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;// 內(nèi)部填充顏色

     [_holeShapeView.layer addSublayer:tubeShape];
    
    NSArray *title = TITLE;
    // for 循環(huán)繪制六個(gè)灰色小圓跟綠色小圓救赐,創(chuàng)建六個(gè)btn,下標(biāo)題并添加進(jìn)數(shù)組
    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) {
           // 將第一個(gè)綠色小圓添加到底層view上
            [_holeShapeView.layer addSublayer:leftSemiShape2];
        }
        
        float x = 4 +(_yy+_xx*i)*i;
        // 創(chuàng)建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];
        
        // 創(chuàng)建下標(biāo)題
        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 . 創(chuàng)建滑動(dòng)控制器view涧团,并添加滑動(dòng)手勢只磷。

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

}

//在移動(dòng)過程中经磅,UIGestureRecognizerStateChanged 這個(gè)狀態(tài)會(huì)調(diào)用很多次,在這里面處理綠色細(xì)長矩形的繪制钮追,添加或刪除綠色小圓layer。

//在移動(dòng)結(jié)束時(shí),UIGestureRecognizerStateEnded 這個(gè)狀態(tài)只調(diào)用一次畦韭,在這里處理最終的綠色細(xì)長矩形弯予,綠色小圓,下標(biāo)題的點(diǎn)亮刊棕,吸附功能炭晒。

- (void)panGesture:(UIPanGestureRecognizer *)gesture
{
    CGFloat y;
   
    switch (gesture.state) {
            
        case UIGestureRecognizerStateBegan:
        {
            
            CGRect rect = gesture.view.frame;
            y = rect.origin.y ;
        }
            break;
        case UIGestureRecognizerStateChanged:
        {
            // 獲得添加手勢的對象
            // 獲得滑動(dòng)的距離  包含 x y 移動(dòng)的數(shù)值
            CGPoint point  =[gesture translationInView:gesture.view];
            
            CGRect targetRect = _targetView.frame;
            
            CGFloat targetX = targetRect.origin.x;

           // 綠色的細(xì)長矩形
            [_recPath removeAllPoints];// 這個(gè)方法會(huì)調(diào)用很多次,每次調(diào)用都會(huì)繪制一條路徑甥角,為了實(shí)現(xiàn)綠色路徑跟隨滑動(dòng)控制器而動(dòng)的效果网严,所有每次繪制之前都移除掉所有的點(diǎn),其它地方有這樣的處理都是一個(gè)道理嗤无。
            [_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) {
                    // 滑動(dòng)過程中添加和刪除綠色圓layer
                    if (targetX >= [self.btnOriginXArr[i]integerValue] && targetX < [_btnOriginXArr[i+1]integerValue]) {
                        // 刪除上一個(gè)綠色小圓layer
                        CAShapeLayer *layer = self.btnLayerArr[i+1];
                        if (layer) {
                            [layer removeFromSuperlayer];
                        }
                        // 添加新的綠色小圓layer
                        [_holeShapeView.layer addSublayer:self.btnLayerArr[i]];
                        [_shapeViewDelegate onShapeViewDelegateEventWithString:titleArr[i]];// 調(diào)用代理方法震束,回調(diào)期數(shù)
                    }
                    
                }
                
            }
            //CGRectOffset是以試圖的原點(diǎn)為起始 移動(dòng) dx x移動(dòng)距離  dy y移動(dòng)距離
            
           gesture.view.frame =CGRectOffset(gesture.view.frame, point.x, y );// 改變滑動(dòng)控制器的frame,只改變X当犯,Y坐標(biāo)保持不變垢村。
            
            //清空移動(dòng)距離
            [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在第一個(gè)圓
            if (targetX<0) {
                
                targetRect.origin.x = 0;
                
                _targetView.frame = targetRect;
                
                [_shapeViewDelegate onShapeViewDelegateEventWithString:@"1期"];
                // 改變下標(biāo)題顏色
                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在最后一個(gè)圓
            if (targetX >btnX) {
                
                targetRect.origin.x = btnX;
                
                _targetView.frame = targetRect;
                [_shapeViewDelegate onShapeViewDelegateEventWithString:@"12期"];
                // 改變下標(biāo)題顏色
                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 在中間各個(gè)圓
            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]];
                        
                        // 改變下標(biāo)題顏色
                        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];
                    }  
                }
            }
           // 先移除貝塞爾所有的點(diǎn),然后重新繪制貝塞爾路徑
            [_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 . 處理按鈕的點(diǎn)擊事件。

- (void)onBtnClick:(UIButton *)btn
{
    NSArray *titleArr = TITLE;
    
    [_shapeViewDelegate onShapeViewDelegateEventWithString:titleArr[btn.tag]];// 回調(diào)代理
    
// 滑動(dòng)控制器frame動(dòng)畫
    [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) {
        // 改變下標(biāo)題顏色
        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的動(dòng)畫沒處理好嚎卫,這里通過延遲處理嘉栓,實(shí)現(xiàn)相關(guān)功能,下次layer動(dòng)畫處理好了再補(bǔ)充上來拓诸。
    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]];
            
        }
        // 先移除貝塞爾所有的點(diǎn),然后重新繪制貝塞爾路徑
        [_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];
        
        
    });

}

具體使用方法

下載好我的demo,在工程中導(dǎo)入DCSliderView類胸懈,設(shè)置代理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];
    
}
需要注意的一點(diǎn)是胚宦,由于各個(gè)小圓之間的間距是逐漸增大的首有,所以我根據(jù)屏幕的寬度設(shè)置了幾個(gè)不同的系數(shù)去適配,如果你沒有使用我代碼中的寬度枢劝,適配就會(huì)出現(xiàn)問題井联。其實(shí)本文只是一個(gè)引子,主講設(shè)計(jì)思路您旁,你可以按照自己的實(shí)際情況去具體設(shè)計(jì)烙常。當(dāng)然,如果你不想動(dòng)手修改的話,那就得按照我設(shè)計(jì)的來蚕脏。

轉(zhuǎn)載請注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侦副,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子驼鞭,更是在濱河造成了極大的恐慌秦驯,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挣棕,死亡現(xiàn)場離奇詭異译隘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)洛心,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門固耘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人词身,你說我怎么就攤上這事玻驻。” “怎么了偿枕?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵璧瞬,是天一觀的道長。 經(jīng)常有香客問我渐夸,道長嗤锉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任墓塌,我火速辦了婚禮瘟忱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苫幢。我一直安慰自己访诱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布韩肝。 她就那樣靜靜地躺著触菜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哀峻。 梳的紋絲不亂的頭發(fā)上涡相,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音剩蟀,去河邊找鬼催蝗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛育特,可吹牛的內(nèi)容都是我干的丙号。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼犬缨!你這毒婦竟也來了喳魏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤遍尺,失蹤者是張志新(化名)和其女友劉穎截酷,沒想到半個(gè)月后涮拗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乾戏,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年三热,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鼓择。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡就漾,死狀恐怖呐能,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抑堡,我是刑警寧澤摆出,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站首妖,受9級特大地震影響偎漫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜有缆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一象踊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棚壁,春花似錦杯矩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至曼验,卻和暖如春逆害,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚣驼。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工魄幕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颖杏。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓纯陨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子翼抠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容