老司机出品———疯狂造轮子之滑动验证码
消失了好久,大家放心,我还活着。
要问我为什么消失了这么久,如果你知道什么叫封闭开发或许你会懂我。
笑不出来
然而最近一直也没时间搞什么飞机,也没有什么能拿出来跟大家分享的,就把最近开发过程中写的一些小东西贴出来给大家看吧。
因为东西比较少,而且没有什么新鲜的技术点,所以老司机先把效果图放出来,这样的话如果你不感兴趣可能看到这就够了。
老司机这里还是主推DWCoreTextLabel,他是一个基于CoreText的异步绘制的图文混排控件,并且支持图片的异步加载与缓存,基本上可以完美的实现图文混排需求。你值得使用。
DWCheckBox就是单选复选框了,也是一个快捷使用并且有着高定制型的类库。
继承UIControl重新实现一个Slider
广告打完了咱们来看第二环节,slider。
最初的时候其实我就是想实现后面那个步进Slider,最初的想法继承UISlider去重写,奈何转了一大圈,各种私有属性用一遍也无法完美的完成我的需求。
主要是由于UISlider中对于滑块和滑竿的定制性很困难,所以自己重写一个Slider吧。
所以为什么想到继承自UIControl去写呢?第一是UISlider继承自UIControl,第二是UIControl封装了-addTarget:selector:events
以及事件追踪的一系列方法。
其实UIControl有四个核心的方法,是用于控制事件追踪的。
///判断是否开始事件追踪
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
///判断事件追踪是否继续
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
///事件追踪取消时处理
-(void)cancelTrackingWithEvent:(UIEvent *)event;
///事件追踪结束时处理
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event;
使用方法无非就是判断当视图接收到事件是如何追踪,可以看一下老司机写Slider的处理。
#pragma mark --- tracking Method ---
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView:self];
location = [self.thumbLayer convertPoint:location fromLayer:self.layer];
if ([PathWithBounds(self.thumbLayer.bounds, FitCornerRadius(self.thumbLayer, self.thumbCornerRadius)) containsPoint:location]) {
self.clickOnThumb = YES;
return YES;
}
location = [self.trackBgLayer convertPoint:location fromLayer:self.thumbLayer];
if ([PathWithBounds(self.trackBgLayer.bounds, FitCornerRadius(self.trackBgLayer, self.trackCornerRadius)) containsPoint:location]) {
self.clickOnThumb = NO;
return YES;
}
return NO;
}
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView:self];
CGFloat margin = FitMarginForThumb(self.thumbSize, [self thumbMarginForBounds:self.bounds]);
location.x -= margin;
CGFloat actualW = CGRectGetWidth([self trackRectForBounds:self.bounds]) - margin * 2;
if (location.x < 0) {
location.x = 0;
} else if (location.x > actualW) {
location.x = actualW;
}
CGFloat percent = location.x / actualW;
CGFloat value = self.minimumValue + (self.maximumValue - self.minimumValue) * percent;
if (value == self.value) {
return YES;
}
_value = value;
[self sendActionsForControlEvents:UIControlEventValueChanged];
if (self.clickOnThumb) {
[self updateValueAnimated:NO];
return YES;
} else {
[self updateValueAnimated:YES];
self.clickOnThumb = NO;
return NO;
}
}
-(void)cancelTrackingWithEvent:(UIEvent *)event {
self.clickOnThumb = NO;
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
self.clickOnThumb = NO;
}
处理过事件追踪,我们只要处理好视图相关的内容即可。
此处可以分为两种思路,一种是通过DrawRect方法去追踪行为后不断绘制,另一种是通过Layer展示各个图层并追踪行为。这里呢,老司机更加推荐使用Layer去处理图层,因为本身DrawRect方法中的代码是使用CPU进行预算然后将bitmap提交给GPU,他处理绘制的速度远不如CALayer直接使用GPU来的快。
图层的绘制老司机在CoreAnimation系列中已经写得很细了,在这也就不多写了。
老司机重写的DWSlider是一个UISlider的替换类,它具备UISlider的所有功能,并且还能自由定制你的Slider的各个属性,相比UISlider来讲可玩性更强,老司机这里放一个传送门。
步进Slider
DWStepSlider是一个分段的Slider,继承自DWSlider。
主要是实现分段的Slider至实现,主要思想还是通过更改事件追踪后的赋值。
#pragma mark --- tracking Method ---
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView:self];
location = [self.thumbLayer convertPoint:location fromLayer:self.layer];
if ([PathWithBounds(self.thumbLayer.bounds, FitCornerRadius(self.thumbLayer, self.thumbCornerRadius)) containsPoint:location]) {
self.clickOnThumb = YES;
return YES;
}
location = [self.trackBgLayer convertPoint:location fromLayer:self.thumbLayer];
if ([PathWithBounds(self.trackBgLayer.bounds, FitCornerRadius(self.trackBgLayer, self.trackCornerRadius)) containsPoint:location]) {
self.clickOnThumb = NO;
return YES;
}
return NO;
}
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView:self];
CGFloat margin = FitMarginForThumb(self.thumbSize, [self thumbMarginForBounds:self.bounds]);
location.x -= margin;
CGFloat actualW = CGRectGetWidth([self trackRectForBounds:self.bounds]) - margin * 2;
if (location.x < 0) {
location.x = 0;
} else if (location.x > actualW) {
location.x = actualW;
}
CGFloat percent = location.x / actualW;
CGFloat value = self.minimumValue + (self.maximumValue - self.minimumValue) * percent;
if (value == self.value) {
return YES;
}
[self setValue:value updateThumb:NO];
[self sendActionsForControlEvents:UIControlEventValueChanged];
if (self.clickOnThumb) {
[self updateValueAnimated:NO];
return YES;
} else {
[self setValue:FixValue(value, _nodes.count) updateThumb:NO];
[self updateValueAnimated:YES];
return NO;
}
}
-(void)cancelTrackingWithEvent:(UIEvent *)event {
if (self.clickOnThumb) {
self.value = FixValue(self.value, _nodes.count);
self.clickOnThumb = NO;
}
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
if (self.clickOnThumb) {
self.value = FixValue(self.value, _nodes.count);
self.clickOnThumb = NO;
}
}
至于图形还是CAShapeLayer的各种形状,老司机也早就说过了,还是传送门吧。
好吧,今天其实也没什么新鲜内容,毕竟都是一些UI控件的封装。
不过也是捋一下思路,控件要如何封装,所以还是不要脸的发出来了。
喜欢哪个给哪个Star吧恩,就是这么好意思