这些坑踩的好疼
个人的域名和blog搭建好了有一段时间,但是一直没有抽时间来写自己的blog。所以就抽了一点时间来记录一下,最近项目中遇到的一些坑。言归正传,最近的项目中,UI的设计图里出了一个渐变色的按钮,而且按钮点击的时候还需要一个透明度为0.3的黑色遮盖在渐变色上。然后,坑就开始了。
第一个大坑(hitTest:withEvent:)
我实现的大体思路是把渐变色绘制成image设置成按钮的背景图,然后,在button上添加一个view,没点击时,设置为透明色,点击时设置为alpha为0.3的黑色。因为点击button的事件会被view给拦截掉。所以,我就在自己创建的button里重写了如下方法:
1 2 3 4 5 6 7 8 9
| #pragma mark - 转换点击 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { // 1.判断当前控件能否接收事件 if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil; // 2.判断点在不在coverView上 if ([self.coverView pointInside:point withEvent:event]) return self; // 3. 判断点在不在当前控件 if ([self pointInside:point withEvent:event] == NO) return nil; }
|
- 第一个判断是如果当前的视图不能交互,被隐藏,或者alpha值<0.01的话,此次Touch操作初始点所在的视图就返回为nil。
- 第二个判断是调用
pointInside: withEvent:
方法,判断当前touch的点在不在添加到button上的view上,如果在上面,就返回button,让button来响应点击
- 第三个判断如果当前的点击不在覆盖的view上就返回nil.
刚开始的时候,并没有测试出问题,然后一次偶然的点击触发了一系列的问题。刚开始只是在一个包含我的渐变色按钮的页面点击出现了奔溃,我们并没有往我的按钮那里去想,最后在其他界面点击也会出现奔溃,控制台打印出来的奔溃信息是[UIWindow dealloced]
;
内心很奔溃,我们定位了很久,比较不同的版本,修改按钮为普通按钮,最后终于定位到了上述方法上。
触发这个问题的方式就是,触碰按钮的边缘,然后就会100%的复现这个问题,分析原因应该就是我的最后一个判断,处理得太唐突,当点不在button或者coverView上的时候,直接返回了nil。最后利用
1 2
| CGPoint subPoint = [subview convertPoint:point fromView:self]; UIView *result = [subview hitTest:subPoint withEvent:event];
|
处理了一下,就好了。但是最后由于怕有疏忽。所以放弃了这种方式处理。然后就有了第二种坑。
第二个坑(CGColorSpaceRelease(colorSpace))
在一个坑中,我最后放弃了在button上添加遮盖的处理方式,而是在点击button的时候,先是将渐变色绘制成图片,再将遮盖色绘制成图片,最后将两张图片绘制成一张图片,设置为button的背景色。核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #pragma mark --渐变色 + (UIImage *)setGradualChangeColor:(NSArray *)colors startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint frame:(CGRect)frame { NSMutableArray *cgColors = [NSMutableArray array]; for(UIColor *c in colors) { [cgColors addObject:(id)c.CGColor]; } UIGraphicsBeginImageContextWithOptions(frame.size, YES, 1); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]); CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)cgColors, NULL); CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); CGGradientRelease(gradient); CGContextRestoreGState(context); // CGColorSpaceRelease(colorSpace); UIGraphicsEndImageContext(); return image; }
#pragma mark - 设置带有阴影的渐变色 - (UIImage *)createGraduallyCoverImage:(UIImage *)graduallyImage coverImage:(UIImage *)coverImage { UIGraphicsBeginImageContextWithOptions(self.size, NO, 2); [graduallyImage drawInRect:CGRectMake(0, 0, self.width, self.height)]; [coverImage drawInRect:CGRectMake(0, 0, self.width, self.height)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
|
然后在有渐变色按钮的地方,有一定的概率触发崩溃,当时为了定位这个问题,就多点几次按钮,然后打断点,打开了僵尸对象调试,发现打印的奔溃信息是:
Assertion failed: (!state->is_singleton), function color_space_state_dealloc, file ColorSpaces/CGColorSpace.c, line 127.
然后就开始在网上找相关的问题,然后在一篇blog中找到了症结所在。具体的原因就是,第一个方法中我注释掉的那行代码。在苹果的api中指出,如果你需要维持这个实例,retain 它,如果没有 retain ,不要 release 它。所以由于我将其release掉了,就会有一定的几率触发奔溃。处理方式,就是注释掉CGColorSpaceRelease(colorSpace);
这行代码,就OK了。
总结
由于在以前的开发中很少手动处理hitTest:withEvent:
,以及接触绘图这一块,所以在开发中出现了上述的问题,还好在互联网发达的今天,能够快速的在网上找到解决方案。对于不太懂hitTest:withEvent:
的,可以看看这一篇文章,作者写的很到位。当时还看了一篇讲解convertPoint: fromView:
和convertPoint: toView:
几个方法的作用的文章。也可以在网上找找其他的看看。
最后,谢谢这些文章的作者,正是有了他们的经验,才为我们这些后来的开发者,提供了宝贵的经验。