仿照汽车之家iOS客户端“找车”栏目的价格区间选择控件,最终实现效果如下:
一、界面实现
*根据屏幕大小以及刻度的大小,宏定义需要用到的一些值
#define SCREENW [UIScreen mainScreen].bounds.size.width
#define SCREENH [UIScreen mainScreen].bounds.size.height
#define PRICEBGW 271.0
#define PRICEBGH 21.0
#define PRICEBGX (SCREENW - PRICEBGW)*0.5
#define PRICEBGY (SCREENH - PRICEBGH)*0.5
#define PRICEMAX (SCREENW*0.5 + PRICEBGW*0.44)
#define PRICEMIN (SCREENW*0.5 - PRICEBGW*0.45)
#define NODE1 (PRICEBGX + 103)
项目中用了两张图片,一张是刻度图(紫色框中的UIIMageView,图片命名为priceRuler@2x.png),一张是把手图(两个红色框内分别有一个UIIMageView,图片命名为xiabashou@2x.png),蓝色框内的进度条,使用一个UIView来实现,而使用UILabel来显示用户选择的数值范围。
priceRuler@2x.png
xiabashou@2x.png
我们把界面实现的代码写到一个函数中,函数名为setUpView,并在viewDidLoad中调用。
-(void)setUpView{
//数值显示
resultLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, SCREENW, 60)];
[resultLabel setTextAlignment:NSTextAlignmentCenter];
[resultLabel setFont:[UIFont systemFontOfSize:18]];
[self.view addSubview:resultLabel];
//刻度图
UIImageView *priceBg = [[UIImageView alloc] initWithFrame:CGRectMake(PRICEBGX, PRICEBGY, PRICEBGW, PRICEBGH)];
[priceBg setImage:[UIImage imageNamed:@"priceBg"]];
[self.view addSubview:priceBg];
//蓝色进度条
progressView = [[UIView alloc] initWithFrame:CGRectMake(PRICEBGX, CGRectGetMaxY(priceBg.frame)-2, PRICEBGW, 2.f)];
[progressView setBackgroundColor:[UIColor colorWithRed:30.0/255.0 green:144.0/255.0 blue:255.0/255.0 alpha:1.0]];
[self.view addSubview:progressView];
//左把手
CGFloat commonHandImageViewW = 20.f;
CGFloat commonHandImageViewH = 25.f;
CGFloat leftHandImageViewX = PRICEBGX - commonHandImageViewW*0.5;
CGFloat leftHandImageViewY = PRICEBGY + commonHandImageViewH;
leftHandImageView = [[UIImageView alloc] initWithFrame:CGRectMake(leftHandImageViewX, leftHandImageViewY, commonHandImageViewW, commonHandImageViewH)];
[leftHandImageView setImage:[UIImage imageNamed:@"xiabashou"]];
[self.view addSubview:leftHandImageView];
//右把手
CGFloat rightHandImageViewX = CGRectGetMaxX(priceBg.frame) - commonHandImageViewW*0.5;
CGFloat rightHandImageViewY = leftHandImageViewY;
rightHandImageView = [[UIImageView alloc] initWithFrame:CGRectMake(rightHandImageViewX, rightHandImageViewY, commonHandImageViewW, commonHandImageViewH)];
[rightHandImageView setImage:[UIImage imageNamed:@"xiabashou"]];
[self.view addSubview:rightHandImageView];
}
现在运行项目,已经可以看到一个区间选择器了,但是我们在屏幕进行任意拖动操作,界面并没有任何改变。因此我们需要使得app能够响应我们的手势。
二、响应用户手势
为了响应用户的拖动手势,给两个把手(UIIMageView)分别添加滑动手势识别(UIPanGestureRecognizer)。我们在setUpView中继续添加如下代码
-(void)setUpView{
….….
//给左把手添加滑动手势识别
UIPanGestureRecognizer *leftPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(leftHandMove:)];
[leftPanRecognizer setMinimumNumberOfTouches:1];
[leftPanRecognizer setMaximumNumberOfTouches:1];
[leftHandImageView setUserInteractionEnabled:YES];
[leftHandImageView addGestureRecognizer:leftPanRecognizer];
//给右把手添加滑动手势识别
UIPanGestureRecognizer *rightPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(rightHandMove:)];
[rightHandImageView setUserInteractionEnabled:YES];
[rightHandImageView addGestureRecognizer:rightPanRecognizer];
}
实现leftHandMove:和rightHandMove:方法,处理滑动事件:
-(void)leftHandMove:(UIPanGestureRecognizer *)pan{
CGPoint point = [pan translationInView:leftHandImageView];
CGFloat x = leftHandImageView.center.x + point.x;
if(x > PRICEMAX){
x = PRICEMAX;
}else if (x< PRICEBGX ){
x = PRICEBGX;
}
leftHandImageView.center = CGPointMake(ceilf(x), leftHandImageView.center.y);
[pan setTranslation:CGPointZero inView:self.view];
}
-(void)rightHandMove:(UIPanGestureRecognizer *)pan{
CGPoint point = [pan translationInView:rightHandImageView];
CGFloat x = rightHandImageView.center.x + point.x;
if(x>PRICEBGX+PRICEBGW){
x = PRICEBGX+PRICEBGW;
}else if (x<PRICEMIN){
x = PRICEMIN;
}
rightHandImageView.center = CGPointMake(ceilf(x), rightHandImageView.center.y);
}
在leftHandMove:方法中,我们使用translationInView函数,得到在指定的View坐标系中的改变值point,将原来的x坐标值加上改变的值后,若超出符合要求的刻度范围,我们要设置其为边界值,然后再更新把手的位置,否则直接更新即可。rightHandMove:同理实现。
尝试运行项目并拖动把手,现在把手的位置可以水平拖动了,但是UILabel还没有显示我们选中的范围,蓝色进度条也没随之改变。
三、更新数值显示和蓝色进度条
为了得到上一步中用户选中的值,我们先声明两个个全局变量:
float leftValue;
float rightValue;
- (void)viewDidLoad {
[super viewDidLoad];
leftValue = 0;
rightValue = 100;
[self setUpView];
}
<pre code_snippet_id="1582410" snippet_file_name="blog_20160219_8_6433823" name="code" class="objc">-(void)leftHandMove:(UIPanGestureRecognizer *)pan{
……
leftValue = x;
</pre><pre code_snippet_id="1582410" snippet_file_name="blog_20160219_9_4132529" name="code" class="objc">}
现在,我们写一个更新数值和进度条的函数,函数名为updateData。
-(void)updateData{
[resultLabel setText:[NSString stringWithFormat:@"%.0f~%.0f",leftValue,rightValue]];
}
我们再一次运行程序,滑动手柄,数值虽然改变了,但并不是我们想要的价格数值!!
这是因为屏幕的坐标和刻度图的坐标范围并不一致,因此我们需要将数据处理一下。可能你已经发现,刻度图中的范围并不是均匀分布的,而是分成三段:0~25、25~40、40~100。因此我们在处理时需要分段处理。
我们写一个将左边转换为价格的转换函数:
//坐标->价格
-(CGFloat)x2price:(CGFloat)x{
CGFloat price = 0.f;
//<5
if(x < PRICEMIN){
price = 0;
}
//5~25
else if (x < PRICEBGX + 133){
price = (x - PRICEMIN) / 120 * 20 + 5;
}
//25~40
else if (x < PRICEBGX + 163){
price = (x - PRICEBGX - 133) *0.5 + 25;
}
//40~100
else if (x < PRICEBGX + 253){
price = (x - PRICEBGX - 163) * 2 / 3 + 40;
}
//100+
else{
price = 100;
}
return price;
}
将leftHandMove:中的leftValue = x;修改为如下
-(void)leftHandMove:(UIPanGestureRecognizer *)pan{
……
leftValue = [self x2price:ceilf(x)];
}
同理rightHandMove:也做相应处理。重新运行程序,滑动把手,显示的价格数值正是我们所期望的。
我们还要更新蓝色进度条,只需修改它的frame即可:
-(void)updateData{
[resultLabel setText:[NSString stringWithFormat:@"%.0f~%.0f",leftValue,rightValue]];
CGRect progressRect = CGRectMake(leftHandImageView.center.x, progressView.frame.origin.y, rightHandImageView.center.x - leftHandImageView.center.x, progressView.frame.size.height);
progressView.frame = progressRect;
}
四、优化
当将右边的把手一直往左滑动,它将滑到左边把手的左边,也就是用户选择的区间中,上界值比下界值更小了!这并不是我们期望的结果。我们希望上界至少要比下界大1个单位,所以当用户滑动右把手到上界比下界小于等于1时,左把手也要跟着滑动,与右把手始终保持1个单位。
我们在rightHandMove:函数中添加如下代码:
-(void)rightHandMove:(UIPanGestureRecognizer *)pan{
……
if (rightValue-leftValue <= 1) {
leftValue = rightValue - 1;
leftHandImageView.center = CGPointMake([self price2x:leftValue], leftHandImageView.center.y);
}
}
同理在leftHandMove:函数中添加对应代码:
-(void)leftHandMove:(UIPanGestureRecognizer *)pan{
……
if (rightValue-leftValue <= 1) {
rightValue = leftValue + 1;
rightHandImageView.center = CGPointMake([self price2x:rightValue], rightHandImageView.center.y);
}
}
注意到函数中用到了方法price2x:,这个函数是将价格转换为对应的x坐标,作用于前面用到的x2price:刚好相反。
//价格->坐标
-(CGFloat)price2x:(CGFloat)price{
CGFloat x;
//<5
if (price<5) {
x = PRICEBGX;
}
//5~25
else if (price >= 5 && price < 25) {
x = (price-5) * 6 + PRICEMIN;
}
//25~40
else if (price >= 25 && price <40) {
x = (price-25) * 2 + 133 + PRICEBGX;
}
//40~100
else if (price >=40 && price <100){
x = (price-40) * 3/2 +163 + PRICEBGX;
}else if(price >= 100){
x = PRICEBGX + PRICEBGW;
}
return x;
}
运行程序,现在已经不能将右把手拖动到左把手的前面的吧~
完整源代码下载:http://download.youkuaiyun.com/detail/dolacmeng/9436673