本课程主要介绍如何实现一个双向滑块,最终的效果如下:
Part I 双向滑块的整体结构
从上图观察可得,整个双向滑块控件共由四部分组成,如下图。
View:整个双向滑块控件。
sliderRect:白条,未被选中的区域。
selectRect:黑条,被选中的区域。
startRect:红条,左滑块。
endRect:蓝条,右滑块。
Part II 双向滑块的初始化及绘制
1、四个区域的初始化
barWidth = NSWidth(self.bounds)*0.03;
sliderRect = NSMakeRect(barWidth,
NSHeight(self.bounds)/4,
NSWidth(self.bounds)-barWidth*2,
NSHeight(self.bounds)/2);
startRect = NSMakeRect(NSMinX(self.bounds),
NSMinY(self.bounds),
barWidth,
NSHeight(self.bounds));
endRect = NSMakeRect(NSMaxX(self.bounds)-barWidth,
NSMinY(self.bounds),
barWidth,
NSHeight(self.bounds));
selectRect = NSMakeRect(barWidth,
NSHeight(self.bounds)/4,
(NSWidth(self.bounds)-barWidth),
NSHeight(self.bounds)/2);
2、四个区域的绘制
对绘制NSRect区域的代码做一层封装,其方法定义如下:
-(void)drawRectBound:(NSRect)bound color:(NSColor*)color{
NSBezierPath *path = [NSBezierPath bezierPathWithRect:bound];
[color setFill];
[path fill];
}
因此,在drawRect方法中调用此方法即可,代码如下:
[self drawRectBound:sliderRect color:[NSColor whiteColor]];
[self drawRectBound:selectRect color:[NSColor blackColor]];
[self drawRectBound:startRect color:[NSColor redColor]];
[self drawRectBound:endRect color:[NSColor blueColor]];
Part III 实现左右滑块的移动
在实现滑块的移动时,需要使用以下四个方法:
//返回YES的话,View会接收mouseDown消息。
-(BOOL)acceptsFirstMouse:(NSEvent *)event{
return YES;
}
//鼠标按下时,执行此方法。
-(void)mouseDown:(NSEvent *)event{
NSPoint clickedLocationPoint = [event locationInWindow];
NSPoint localPoint = [self convertPoint:clickedLocationPoint fromView:nil];
if(NSPointInRect(localPoint, startRect)){
//左滑块被点击
startChange = YES;
}else if(NSPointInRect(localPoint, endRect)){
//右滑块被点击
endChange = YES;
}
}
//鼠标松开时,执行此方法。
-(void)mouseUp:(NSEvent *)event{
startChange = NO;
endChange = NO;
}
//鼠标拖拽时执行此方法。
-(void)mouseDragged:(NSEvent *)event{
NSPoint clickLocationPoint = [event locationInWindow];
NSPoint localPoint = [self convertPoint:clickLocationPoint fromView:nil];
if(startChange){
[self leftBarChange:localPoint];
}else if(endChange){
[self rightBarChange:localPoint];
}
}
在leftBarChange和rightBarChange方法中,我们需要通过坐标的计算重新绘制双向滑块的四个区域,代码如下:
-(void)leftBarChange:(NSPoint)localPoint{
NSRect origin = startRect;
if(localPoint.x-barWidth < NSMinX(self.bounds)){
//超出最小值
startRect = NSMakeRect(0,
NSMinY(origin),
NSWidth(origin),
NSHeight(origin));
}else if(localPoint.x > NSMinX(endRect)){
//超出最大值
startRect = NSMakeRect(NSMinX(endRect)-barWidth,
NSMinY(origin),
NSWidth(origin),
NSHeight(origin));
}else{
//正常值
startRect = NSMakeRect(localPoint.x-barWidth,
NSMinY(origin),
NSWidth(origin),
NSHeight(origin));
}
selectRect = NSMakeRect(NSMaxX(startRect),
NSHeight(self.bounds)/4,
NSMinX(endRect)-NSMaxX(startRect),
NSHeight(self.bounds)/2);
[self setNeedsDisplay:YES];
}
-(void)rightBarChange:(NSPoint)localPoint{
NSRect origin = endRect;
if(localPoint.x < NSMaxX(startRect)){
//超出最小值
endRect = NSMakeRect(NSMaxX(startRect),
NSMinY(origin),
NSWidth(origin),
NSHeight(origin));
}else if(localPoint.x+barWidth > NSMaxX(self.bounds)){
//超出最大值
endRect = NSMakeRect(NSMaxX(self.bounds)-barWidth,
NSMinY(self.bounds),
barWidth,
NSHeight(self.bounds));
}else{
//正常值
endRect = NSMakeRect(localPoint.x,
NSMinY(origin),
NSWidth(origin),
NSHeight(origin));
}
selectRect = NSMakeRect(NSMaxX(startRect),
NSHeight(self.bounds)/4,
NSMinX(endRect)-NSMaxX(startRect),
NSHeight(self.bounds)/2);
[self setNeedsDisplay:YES];
}
Part IV 计算左右滑块的选中范围值
当左右滑块的位置发生变化时,需要计算左右滑块当前位置的值,其公式如下:
//计算左滑块起始值
double startScale = (NSMaxX(startRect)-NSMinX(sliderRect))/NSWidth(sliderRect);
_start = startScale*(self.maxValue-self.minValue)+self.minValue;
//计算右滑块结束值
double endScale = (NSMinX(endRect)-NSMinX(sliderRect))/NSWidth(sliderRect);
_end = endScale*(self.maxValue-self.minValue)+self.minValue;
当设定起始值与结束值时,计算左右滑块位置,即set方法,代码如下:
-(void)setStart:(double)start{
double startScale = (start-self.minValue)/(self.maxValue-self.minValue);
NSPoint tmpPoint = NSMakePoint(startScale*NSWidth(sliderRect) + NSMinX(sliderRect),0);
_start = start;
[self leftBarChange:tmpPoint];
}
-(void)setEnd:(double)end{
double endScale = (end-self.minValue)/(self.maxValue-self.minValue);
NSPoint tmpPoint = NSMakePoint(endScale*NSWidth(sliderRect)+NSMinX(sliderRect),0);
_end = end;
[self rightBarChange:tmpPoint];
}
Part V 声明、实现委托
代理协议声明如下:
@protocol MySliderDelegate<NSObject>
- (void) leftBarChanged:(MySlider *)slider;
- (void) rightBarChanged:(MySlider *)slider;
@end
实现的核心代码如下:
//左滑块改变
if(self.delegate != NULL){
if([self.delegate respondsToSelector:@selector(leftBarChanged:)]){
[self.delegate leftBarChanged:self];
}
}
//右滑块改变
if(self.delegate != NULL){
if([self.delegate respondsToSelector:@selector(rightBarChanged:)]){
[self.delegate rightBarChanged:self];
}
}
PS:与本文对应的视频版课程正在积极地筹备中,想获取本项目代码的小伙伴们,请点击下方的喜欢并私信我,我会一一回复给各位小伙伴。