前言
前些天看到一個(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è)計(jì)思路
先添加一個(gè)底層view入录,然后在底層view上畫出背景l(fā)ayer,這里是六個(gè)小圓蛤奥,和一個(gè)細(xì)長矩形。
小圓點(diǎn)是可點(diǎn)的僚稿,所以還要?jiǎng)?chuàng)建六個(gè)btn,并添加下標(biāo)題凡桥。
在底層view的上方添加一個(gè)view,充當(dāng)滑動(dòng)控制器蚀同。
在滑動(dòng)控制器上添加拖拽手勢缅刽,并且控制滑動(dòng)時(shí),只改變控制器的X坐標(biāo)蠢络,Y軸保持不變衰猛。
繪制綠色layer跟隨滑動(dòng)控制器而動(dòng)。
處理各個(gè)按鈕的點(diǎn)擊事件刹孔,讓滑動(dòng)控制器跟綠色layer隨之改變啡省。
處理細(xì)節(jié),吸附功能髓霞,點(diǎn)亮下標(biāo)題卦睹,對滑動(dòng)控制器最小和最大X軸位移的控制。
設(shè)置代理方库,在各個(gè)方法里觸發(fā)代理方法结序。
實(shí)現(xiàn)相關(guān)功能
- 創(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)載請注明出處